day43-反射02( 四 )

  • 类加载过程图

day43-反射02

文章插图
  • 类加载各阶段完成的任务
    • 加载阶段:将类的class文件读入内存,并为之创建一个java.lang.Class对象 。此过程由类加载器完成 。
    • 连接阶段:将类的二进制数据合并到jre中
    • 初始化阶段:JVM负责对类进行初始化,这里主要是指静态成员
6.1.1加载阶段
day43-反射02

文章插图
JVM 在该阶段的主要目的是,将字节码从不同的数据源(可能是class文件,也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并声称一个代表该类的java.lang.Class对象
day43-反射02

文章插图
6.1.2连接阶段-验证
day43-反射02

文章插图
  1. 目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全
  2. 包括:文件格式验证(是否以 魔数 oxcafebabe开头)、元数据验证、字节码验证和符号引用验证

day43-反射02

文章插图
  1. 可以考虑使用 -Xverify:none 参数关闭大部分的类验证措施,缩短虚拟机类加载的时间
6.1.3连接阶段-准备
day43-反射02

文章插图
JVM会在该阶段对静态变量,分配内存并默认初始化(对应的数据类型的默认初始值,如0,0L,null,false等) 。这些变量所使用的内存都将在方法区中进行分配
例如:
package li.reflection.classload_;//我们说明一个类加载的链接阶段-准备public class ClassLoad02 {public static void main(String[] args) {}}class A {//属性-成员变量-字段//一个类加载的链接阶段中的准备阶段 属性是如何处理的//1. n1 是实例变量,不是静态变量,因此在准备阶段,是不会分配内存的//2. n2 是静态变量,分配内存 n2,且默认初始化为 0,而不是20//3. n3 是static final,是常量,它和静态变量不一样,因为一旦赋值就不变,n3 = 30public int n1 = 10;public static int n2 = 20;public static final int n3 = 30;}6.1.4连接阶段-解析
day43-反射02

文章插图
虚拟机将常量池内的符号引用替换为直接应用的过程
个人理解 java虚拟机中的符号引用和直接引用_maerdym的博客-CSDN博客
6.1.5初始化阶段
day43-反射02

文章插图
  1. 到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行<clinit>()方法的过程
  2. <clinit>()方法是 由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量 的赋值动作和静态代码块中的语句,并进行合并 。-->例子1
  3. 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕 。
例子1:演示类加载的初始化阶段
package li.reflection.classload_;//演示类加载的初始化阶段public class ClassLoad03 {public static void main(String[] args) {//分析:/*** 1.加载B类,并生成 B的Class对象* 2.链接 :将num默认初始化为 0* 3.初始化阶段:*3.1依次 自动收集类中的 所有静态变量的赋值动作 和 静态代码块中的语句,并合并*收集:*clinit(){*System.out.println("B的静态代码块被执行");*num = 300;*num = 100;*}*合并:num =100;*///直接使用类的静态属性也会导致类的加载System.out.println(B.num);//100}}class B {static {System.out.println("B的静态代码块被执行");num = 300;}static int num = 100;public B() {System.out.println("B的构造器被执行");}}

经验总结扩展阅读