JVM的相关知识

发布于:2024-06-04 ⋅ 阅读:(32) ⋅ 点赞:(0)

目录

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


网站公告

今日签到

点亮在社区的每一天
去签到