目录
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,希望对你有所帮助。