• JVM的相关知识


    目录

    JVM内存划分

    类加载过程

    类加载中的“双亲委派模型”


    JVM内存划分

    JVM也就是java进程。这个进程一旦跑起来之后,就会从操作系统里,申请一大块内存空间。JVM接下来就要进一步的对这个大的空间进行划分。划分成不同区域,从而每个区域都有不同的功能作用。

    具体如何划分的:

    1、堆区    整个内存区域中,最大的区域。放的就是代码中new 出来的对象类中的成员变量存储在堆上。

    2、栈区   分为JVM虚拟机栈本地方法栈,都是保存了方法的调用关系。JVM虚拟机栈中存放的是java代码的调用关系。本地方法栈中存放的是针对JVM内部C++代码的调用关系。局部变量存储在栈区。

    3、元数据区   (以前叫“方法区”,从java8改成了元数据区)放的是“类对象”。代码中写的每个类,在jvm上运行的时候,都会有对应的类对象。还放了方法相关的信息,类有一些方法,每个方法都代表了一系列的“指令集合”(JVM字节码指令)。

    常量池:放的类中定义的常量。

    4、程序计数器    是内存区域中最小的区域,只需要保存当前要执行的下一条指令(JVM字节码)的地址(这个地址就是元数据区里面的一个地址 )。

    基本原则

    一个对象在哪个区域,取决于对应变量的形态。

    1)局部变量  栈上

    2)成员变量  堆上

    3)静态成员变量  方法区/元数据区

    上述四个区域中,堆和元数据区,是整个进程只有一份。栈和程序计数器,是每个线程都有一份的。多个线程共享同一份数据,每个线程的局部变量,则不是共享的,每个线程都是有自己一份的。

    类加载过程

    一个java进程要跑起来,就需要把.java先变成.class文件(硬盘),加载到内存中,得到“类对象”。

    类加载的几个环节

    1)加载:在硬盘上找到对应的.class文件,读取文件内容。

    2)验证:检查.class里的内容,是否符合要求。

    3)准备:给类对象,分配内存空间。(类加载最终要得到的就是类对象)会先把这个空间里的数据先全都填充成0。(此时,如果这个类有静态成员,值就是0)

    4)解析:针对字符串常量来初始化。把.class文件中的常量的内容取出来,放到“元数据区”。

    5)初始化:针对类对象初始化。(不是针对对象初始化,和构造方法无关)会给静态成员初始化。

    此时类对象就加载完成了,后续代码就可以使用这个类对象,创建实例,或者使用里面的静态成员了。

    类加载中的“双亲委派模型

    该模型出现在“加载”环节,根据代码中的“全限定类名”(包名+类名)找到对应的.class文件。

    双亲委派模型描述了JVM加载.class文件过程中,找文件的过程。

      “类加载器”:在JVM中包含的一个特定的模块/类。这个类负责完成后续的类加载工作。

    JVM中内置了3个类加载器,负责加载不同的类。

    1)BootstrapClassLoader

    负责加载标准库的类。

    2) ExtentionClassLoader

    负责加载JVM扩展库里面的类。

    3) ApplicationClassLoader

    负责加载第三方库的类和你自己写的代码的类。

    从上至下三个类加载器依次为父子关系,BootstrapClassLoader为ExtentionClassLoader的父亲,此处的“父子关系”不是通过类的继承表示的(不是父类子类)。而是通过类加载器中存在一个“parent”这样的字段,指向自己的父亲。

    双亲委派模型的工作过程

    例如,给定了一个类的全限定类名,比如java111.Test

    此时加载过程如下:

    1、工作从ApplicationClassLoader开始,ApplicationClassLoader并不会立即开始搜索第三方库的相关目录,而是把任务交给自己的父亲来进行处理。

    2、工作就到了ExtentionClassLoader,也不会立即搜索负责的拓展库的目录,也是把任务交给自己的父亲来处理。

    3、工作就到了BootstrapClassLoader,也想交给自己的父亲来处理,但是它的parent指向null,只能自己处理,BootstrapClassLoader尝试在标准库的路径中搜索上述类。如果这个类,在标准库中找到了,于是搜索过程就完成了,类加载器负责打开文件,读取文件等后续操作就行了。

    如果没找到,任务还是要继续还给儿子来处理。

    4、工作回到了ExtentionClassLoader,此时就要搜索扩展库对应的目录了。如果找到了,就由当前的类加载器负责打开文件,读取文件等后续操作。如果没找到,任务还是要继续交给儿子来处理。

    5、工作又回到了ApplicationClassLoader,此时要搜索第三方库/用户项目代码的目录了。如果找到了,也是由当前的类加载器负责处理。如果没找到,任务还是要继续还给儿子来处理。此时,没有儿子了,还没找到,就会最终抛出一个ClassNotFoundException

    上述过程,主要为了应对这个场景:

    比如你自己代码里写了一个类,类的名字和标准库/拓展库冲突了,JVM会确保加载的类是标准库的类(就不加载你自己写的类了)。

    是否可以打破双亲委派模型?比如自己写个类加载器,是否必须按照上述的流程完成类的查找过程呢?

    可以打破。但实际开发中一般不会自己实现。

    以上,关于JVM,希望对你有所帮助。

  • 相关阅读:
    【LeetCode与《代码随想录》】数组篇:做题笔记与总结-Java版
    设计模式之保护性暂停
    【kkFileView】源码编译打包构建镜像部署
    RabbitMQ中的死信队列产生的几个条件和应用场景
    355. 设计推特
    GIF动图怎么变成jpg动图?一键分解GIF动画
    bat写的git命令大全(适合初学者)掌握命令行下的Git操作!
    Matlab:Matlab 软件学习之GUI图像用户界面简介(工具栏/菜单栏/对话框)、GUI界面设计案例应用(设计二级菜单栏)之详细攻略
    IO流(二)
    自己动手写编译器:汤普森构造法
  • 原文地址:https://blog.csdn.net/m0_73629312/article/details/139213639