这个系列文章是对《深入理解Java虚拟机》一书的笔记及个人理解
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
在Java语言里,类加载和连接过程都是在程序运行期间完成的,这样会在类加载时稍微增加一些性能开销,但是却能为Java应用程序提供高度的灵活性,Java中天生可以动态扩展的语言特性就是依赖运行期动态加载连接这个特点实现的。
类的生命周期
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括了七个阶段:
加载(Loading)
验证(Verification)
准备(Preparation)
解析(Resolution)
初始化(Initialization)
使用(Using)
卸载(Unloading)
其中验证、准备、解析三个部分统称为连接(Linking)。
何时加载类(when)
1、使用new关键字实例化对象
2、读取或者设置一个类的静态变量(final修饰的被放入常量池的静态字段除外)
3、调用静态方法
4、使用反射进行调用的时候
5、初始化一个类的时候,发现其父类还没有被初始化,则需要先触发其父类的初始化
6、执行main方法的类,虚拟机会先初始化这个主类
怎么加载(how)
加载
加载阶段,虚拟机需要完成以下三件事:
1、通过一个类的全限定名来获取定义此类的二进制字节流
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
3、在Java堆中生成一个代表这个类的Class对象,作为方法区这些数据的访问入口。
虚拟机规范并没有指明二进制字节流要从一个Class文件中获取,准确地说是根本没有指明要从哪里获取及怎样获取。因此许多Java技术都建立在这一基础之上。
- 从ZIP包中读取,最终成为Jar、Ear、War格式的基础
- 从网络中获取,典型的场景就是Applet
- 运行时计算生成,典型场景就是动态代理技术
- 由其它文件生成,典型场景就是JSP
- 从数据库中读取,等等
加载过程即可以使用系统提供的类加载器来完成,也可以使用用户自定义的类加载器去完成
连接
连接阶段比较复杂,一般会跟加载阶段和初始化阶段交叉进行,这个阶段的主要任务就是做一些加载后的验证工作以及一些初始化前的准备工作,可以细分为三个步骤:验证、准备和解析。
1、验证
- 文件格式验证,验证字节流是否符合Class文件格式的规范
- 元数据验证,对字节码描述的信息进行语义分析,保证符合Java语言规范
- 字节码验证,整个验证过程中最复杂的一个阶段,主要工作是进行数据流和控制流分析。
- 符号引用验证,发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作在解析阶段发生。
2、准备
为类变量分配内存并设置类变量初始值,这些内存都将在方法区中进行分配。
- 只处理类变量(statis修饰的变量),而不处理实例变量
- 设置初始值
3、解析
将常量池中的符号引用替换为直接引用的过程
初始化
类初始化阶段是类加载过程中的最后一步,前面的类加载过程中,除了在加载阶段用户应用程序可以通过类加载器参与之外,其余动作都是由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码。
在准备阶段,类变量已经赋过一次系统要求的初始值,而在初始化阶段,则是根据程序代码进行初始化类变量和其他资源。