JVM把描述类数据的字节码.Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载。
类从加载进虚拟机内存到使用完从内存中释放,一共经历了 加载、验证、准备、解析、初始化、使用、卸载七个阶段。
其中类加载进虚拟机的过程我们着重了解前五个加载、验证、准备、解析、初始化。下面依次说一说这五个分别做了什么
加载
(加载类的二进制数据)
- 根据各种途径获取类的二进制数据,也就是class文件,把获取到的二进制数据读入内存
- 存储在运行时数据区的方法区,将字节流所代表的静态存储结构转换为方法区的运行时数据结构
- 在方法区中创建相应的java.lang.Class对象用来作为这些数据结构的访问入口。
验证
(确保class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全。)
- 文件格式验证:验证字节流是否符合class文件的规范
- 元数据验证:这个阶段是对字节码描述的信息进行语义分析,以保证起描述的信息符合java语言规范要求。验证点可能包括:这个类是否有父类(除了java.lang.Object之外,所有的类都应当有父类)、这个类是否继承了不允许被继承的类(被final修饰的)、如果这个类的父类是抽象类,是否实现了起父类或接口中要求实现的所有方法等等。
- 字节码验证: 进行数据流和控制流分析,这个阶段对类的方法体进行校验分析,这个阶段的任务是保证被校验类的方法在运行时不会做出危害虚拟机安全的行为。如:保证访法体中的类型转换有效,例如可以把一个子类对象赋值给父类数据类型,这是安全的,但不能把一个父类对象赋值给子类数据类型、保证跳转命令不会跳转到方法体以外的字节码命令上。
-
符号引用验证:这里的符号引用不单单指类的也指方法的,发生在符号引用转为直接引用的时候 对常量池中符号引用的信息进行匹配性校验,确保解析动作正确执性。例如:常量池中符号引用所描述的类是否存在
准备
(正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。)
- 类变量——staic修饰的成员变量
- 像private static int num = 111;这个阶段会将num初始化为0,而不是111(这个会在初始化的时候执行)
解析
(将虚拟机常量池内的符号引用替换为直接引用)
- 解析的内容有类或接口、字段、类方法、接口方法
- 符号引用替换成直接引用 字符串引用对象不一定被加载,直接引用就是指针或地址偏移量,引用对象一定在内存中
初始化
(给已分配空间的属性赋值)
初始化阶段是执行类构造器<clinit>()方法的过程
什么情景下会触发初始化过程。
- new一个实例化对象的时候
- 读取或设置一个类的静态字段的时候(final修饰除外)
- 调用类的静态方法的时候
- 使用java.lang.reflect包的方法对类进行反射调用的时候
- JVM启动时执行main()函数所在的那个类,会先对其初始化