类加载阶段
类加载过程图
加载阶段ClassLoader
类加载阶段是类加载过程的第一个阶段,主要完成以下三件事情:
- 通过类的全名,获取类的二进制字节流。
- 将字节流所代表的静态存储结构转换为方法区的运行时数据结构。
- 在内存中(堆)生成一个代表该类的 Class 对象,作为方法区这些数据的访问入口。
连接Linking
验证阶段
目的是确保Class文件的字节流中信息符合虚拟机要求,并且不会危害虚拟机安全。
包括:文件格式验证、元数据验证、字节码验证等
- 文件格式验证是要验证字节流是否符合class文件格式规范,可以验证的地方主要包括:开头是否是魔数0xCAFEBABE、主次版本号能否被Java虚拟机接受、常量池中的常量是否有不被支持的类型等。
- 元数据验证是对类的元数据信息进行语义分析,以保证其描述符合Java语言规范的要求,可以检查的地方主要包括:这个类是否有父类(除了Object之外)、这个类的父类是否继承了不允许被继承的类(如final修饰的类)、如果这个类不是抽象类,是否实现了其父接口或者抽象类中要求实现的所有方法等。
- 字节码验证是通过数据流和控制流分析,确定程序语义是合法、符合逻辑并且能够正常执行。它可以保证被校验过后的代码不会做出危害虚拟机安全的行为,如访问一个不存在或者不属于自己的字段或方法、越界访问数组、改变常量池中内容等。
准备阶段
JVM机会在这个阶段对静态变量分配内存并默认初始化(默认为0、null、false等),这些变量的内存都会放在方法区内。
//举例说明:
public int n1 = 10;
//1. n1 是实例属性, 不是静态变量,因此在准备阶段,是不会分配内存
public static int n2 = 20;
//2. n2 是静态变量,分配内存 n2 是默认初始化 0 ,而不是 20
public static final int
//3. n3 是 static final 是常量, 他和静态变量不一样, 因为一旦赋值就不变 n3 = 30
解析阶段
符号引用,表达的是一种相对引用,(类似用相对坐标,只知道x引用了y),而解析阶段把他从相对的符号引用变成了直接引用(类似用绝对坐标进行定位,比如一栋楼的001号房引用了002号房)
初始化阶段
初始化阶段是类加载的最后一个阶段,它主要是为类的静态变量赋予正确的初始值,执行类的静态代码块。初始化阶段只有在遇到以下六种情况时才会被触发。
- 遇到new、getstatic、putstatic或invokestatic这四条字节码指令时。
- 使用java.lang.reflect包的方法对类进行反射调用时。
- 当初始化一个类时,如果发现其父类还没有被初始化,则需要先触发其父类的初始化。
- 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
- 当一个接口中定义了JDK 8 新加入的默认方法(被default关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。
//初始化阶段:依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并合并。
class B {
static {
System.out.println("B 静态代码块被执行");
num = 300;
}
static int num = 100;
}
/*
clinit() {
System.out.println("B 静态代码块被执行");
//num = 300;
num = 100;
}
合并: num = 100
*/
//clinit()是线程同步的
标签:初始化,字节,验证,静态,详解,阶段,加载
From: https://www.cnblogs.com/hanlinyuan/p/17234861.html