目录
JVM内存区域划分
Java类的加载过程
双亲委派模型
JVM内存区域划分
JVM中的内存区域共划分为五大部分,分别为虚拟机栈、堆、程序计数器、本地方法栈和方法区,如下边这张图所示:
其中,每个内存区域主要存放的数据及功能如下:
- 虚拟机栈:Java虚拟机栈描述了方法执行的内存模型,每个方法在执行时都会在栈上开辟一个栈帧用于存储局部变量,方法返回地址等。
- 堆:我们在程序中创建的所有对象壹基金其中的成员变量都保存在堆中,堆内存是被线程所共享的。
- 程序计数器:保存了下一条指令的执行地址
- 方法区:方法区中包含了被虚拟机加载的类信息,常量和静态变量以及被编译器编译后的代码等数据,运行时常量池也包含在其中。
- 本地方法栈:与虚拟机栈类似,只不过本地方法栈是给本地方法使用的。
上述的五大区域,除了堆和方法区之外,其他的区域都是线程私有的。
Java类的加载过程
Java类的加载过程共分为三大步:加载(loading)、连接(linking)以及初始化(intialization)。其中连接(linking)阶段又分为验证(verification)、准备(preparation)以及解析(resolution)三个小阶段。这个过程如下面这张图所示:
类加载各个阶段的工作
- Loading加载阶段
通过各种途径将类类的二进制字节码文件读入内存,并创建一个Class类对象- Linking连接阶段
- Verification验证阶段
验证字节码文件中的字节流信息是否符合特定JVM的规范,例如,文件格式验证以及符号引用验证等。如果项目较大,需要加载的类很多,可以在该阶段通过 -Xverify:none;参数关闭大部分类的加载验证阶段来缩短虚拟机类加载的时间达到优化效果- Preparation准备阶段
正式为类中定义的静态变量分配内存并设置初始值,静态变量使用的内存将在方法区中进行分配- Resolution解析阶段
JVM将常量池中的符号引用替换为直接引用- Intialization初始化阶段
编译器手机类中所有的静态代码块以及静态变量的值并执行静态代码块中的代码以及完成静态变量的赋值操作,完成类对象的构建
双亲委派模型
什么是双亲委派模型?
它是处于类加载过程中loading阶段的一个环节。说的是如果一个类加载器接收到了类加载的请求,尝试加载类的过程。
而为了应对不同的类加载场景,Java中已经定义好的不同的类加载器,并且每一个类加载器有自己负责的一片加载区域。
当类加载请求发出后,首先会被最底层的类加载器接收到,并依次将这个请求传递给最顶层的类加载器进行加载,当顶层加载器无法在自己的加载区域中找到这个类时,就会依次反馈给底层类加载器进行加载。
上述类加载器的工作过程就称为双亲委派模型,如下图所示。
关于Java中的类加载器
Java中已经定义好了以下的三个类加载器,它们各自负责的加载区域如下:
BootstrapClassLoader——负责加载JDK标椎库中的类信息
ExtensionClassLoader——负责加载JDK扩展类,像ClassPath执行的所有jar或者目录
ApplicationClassLoader——负责加载程序猿指定的目录中的类信息
以标准库类java.lang.String为例,我们来梳理下双亲委派模型下标椎类的加载过程:
- 首先,ApplicationClassLoader接收到该类的加载请求,它会先检查下自己的父 加载器是否已经加载过了。如果没有,就将加载请求转发给父 加载器ExtensionClassLoader
- ExtensionClassLoader也会检查下自己的父加载器BootstrapClassLoader是否已经加载过了,如果没有,就继续向上转发类加载请求给到BootstrapLoader
- BootstrapClassLoader在接收到请求后从自己的加载区域(即JDK标准目录中)根据类的全限定名称查找该类,找到了,于是进行加载,完成该类的loading阶段的类加载过程
以自定义类java.shuai.Test为例,我们再来梳理下双亲委培模型下自定义类的加载过程:
- 首先,前两步与上述标椎类的加载过程一致
- 接着,BootstrapClassLoader根据该类的全限定名称从自己的加载区域中没有找到,于是反馈给下一层类 加载器ExtensionClassLoader进行加载
- 同理,ExtensionClassLoader在自己的加载区域中也没有找到,于是接着向下反馈给ApplicationClassLoader加载器
- ApplicationClassLoader从自己的加载区域(即程序猿的自定义类目录中)根据该类的全限定名称进行搜索并找到了,于是完成了该类的加载。
如果在上述过程中,父 加载器向下反馈到最底层类加载曾仍然没有找到这个类,就会抛出ClassNotFounfException异常。