JVM运行和类加载全过程
研究的作用
- 有助于了解JVM运行过程
- 更深入了解JVM动态性,提高程序的灵活性
定义
类加载全过程,就是JVM把class文件加载到内存,并对数据进行校验、解析和初始化,最终形成 JVM可以直接使用的Java类型的过程
过程
(1) 加载
将class文件中的字节码内容加载到内存中,并将静态数据转化成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class
对象(可以理解成在堆中生成了类的图纸),作为方法区类数据的访问入口,这个过程需要类加载器的参与。
(2) 链接
将Java类的二进制代码合并到JVM的运行状态之中的过程,分为三个小步
— 验证:确保加载的类信息符合JVM规范,没有安全方面的问题
— 准备:正式为类变量(static 变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分 配。这些初始值都是默认值,并非自定义值
— 解析:将虚拟机常量池内的符号引用替换为直接引用的过程。即指定符号对应数据的存储地址,这个地址 可以是直接地址,也可以是相对地址
(3) 初始化
初始化阶段是执行类构造器<clinit>()
方法的过程。类构造器<clinit>()
方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并并产生的
当初始化一个类的时候,如果发现其父类还未初始化,则需要先发出其父类的初始化
虚拟机会保证一个类的<clinit>()
方法在多线程环境中被正确加锁和同步
类加载的全过程示意图
类的主动引用(发生类的初始化)
(1) new
一个类的对象
(2) 调用类的静态成员(除了final
常量)和静态方法
(3) 使用反射对类进行反射调用
(4) 初始化一个类时,若其父类未初始化,会先初始化父类
类的被动引用(不发生类的初始化)
(1) 当访问一个类的静态域时,只有真正声明这个域的类才会被初始化
(2) 通过数组定义类引用,不会触发此类的初始化
(3) 引用final
常量不会触发此类的初始化
具体代码如下所示,可以逐句去掉注释进行测试
package com.sxt.test;
public class Demo01 {
public static void main(String[] args) {
System.out.println("Demo01的main方法");
//主动引用
//A a = new A();
//System.out.println(A.width);
//被动引用
//System.out.println(A.height);
//A[] as = new A[10];
System.out.println(B.width);
}
}
class A extends A_father{
//由于是从上往下合并,所以先赋值100,后赋值300
public static int width = 100;
public static final int height = 200;
static{
System.out.println("静态初始化类A");
width = 300;
}
public A(){
System.out.println("创建A类的对象");
}
}
class A_father{
static {
System.out.println("静态初始化类A_father");
}
}
class B extends A{
static{
System.out.println("创建B类的对象");
}
}
"静态初始化类A_father");
}
}
class B extends A{
static{
System.out.println("创建B类的对象");
}
}