【JVM】类加载机制:分析与验证

一、加载

  • 将类的字节码载入方法区中,内部采用 C++ 的 instanceKlass 描述 java 类。它的重要 field 有:
    • _java_mirror : java 的类镜像,例如对 String 来说,就是 String.class,作用是把 klass 暴露给 java 使用
    • _super :父类
    • _fields :成员变量
    • _methods :方法
    • _constants :常量池
    • _class_loader :类加载器
    • _vtable :虚方法表
    • _itable :接口方法表
  • 如果这个类还有父类没有加载,先加载父类
  • 加载和链接可能是交替运行的

注意

  • instanceKlass 这样的【元数据】是存储在方法区(1.8 后的元空间内),但 _java_mirror 是存储在堆中
  • 可以通过前面介绍的 HSDB 工具查看

【JVM】类加载机制:分析与验证_第1张图片

二、链接

1)验证

验证类是否符合 JVM规范,安全性检查

  • 修改 Verify.java 的魔数:将 cafebabe 修改为 cafebaby

    【JVM】类加载机制:分析与验证_第2张图片

  • 解释执行结果:类格式错误

    PS F:\software\IDEA\JavaProjects\JvmTest\target\classes\org\example\classLoading\link> java Verify
    Error: A JNI error has occurred, please check your installation and try again
    Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 1128351301 in class file Verify
            at java.lang.ClassLoader.defineClass1(Native Method)
            ...
    

2)准备

为 static 变量分配空间,设置默认值

  • JDK 7 之前:static 变量 存储于 instanceKlass 末尾
  • JDK 7 开始:static 变量 存储于 _java_mirror(类.class对象) 末尾
  • 注意:final 类型的 static 变量
    • 基本类型常量、字符串常量:编译阶段值就确定了,赋值操作在本阶段完成
    • 引用类型常量:赋值操作在 初始化阶段 完成

3)解析

将自己在其他类的常量池中的符号引用解析为直接引用

类A没有解析为直接引用

public class Analysis{
    public static void main(String[] args) throws IOException {
        //1.访问类A的静态变量(基本类型、字符串)不会导致类A的初始化(解析)
        System.out.println(A.a);
        System.in.read();
    }
}
class A{
    final static int a=1;
    static int b=1;
    static {
        System.out.println("init");
    }
}
  1. 运行程序

  2. 执行命令行命令,查看当前进程id

    PS F:\software\IDEA\JavaProjects\JvmTest> jps
    13816 Analysis
    ...
    
  3. 进入 jdk 安装目录,执行命令打开 HSDB工具

    F:\software\Java\jdk1.8.0_333>java -cp ./lib/sa-jdi.jar sun.jvm.hotspot.HSDB
    
  4. 将 HSDB工具 链接到当前进程

    【JVM】类加载机制:分析与验证_第3张图片

  5. 找到 Analysis 类的常量池,查看类A的引用

【JVM】类加载机制:分析与验证_第4张图片

类A解析为直接引用

public class Analysis{
    public static void main(String[] args) throws IOException {
        //2.访问类变量导致初始化
        System.out.println(A.b);
        System.in.read();
    }
}
class A{
    final static int a=1;
    static int b=1;
    static {
        System.out.println("init");
    }
}
  1. 运行程序

  2. 执行命令行命令,查看当前进程id

    PS F:\software\IDEA\JavaProjects\JvmTest> jps
    4768 Analysis
    ...
    
  3. 进入 jdk 安装目录,执行命令打开 HSDB工具

    F:\software\Java\jdk1.8.0_333>java -cp ./lib/sa-jdi.jar sun.jvm.hotspot.HSDB
    
  4. 将 HSDB工具 链接到当前进程

    【JVM】类加载机制:分析与验证_第5张图片

  5. 找到 Analysis 类的常量池,查看类A的引用

    【JVM】类加载机制:分析与验证_第6张图片

三、初始化

1)()V 方法

初始化即调用 ()V ,虚拟机会保证 ()V 的线程安全

2)发生的时机

导致类初始化的情况:

  • main 方法所在的类
  • new
  • 首次访问类的 静态变量、静态方法
  • 子类初始化,如果父类还没初始化
  • Class.forName(String)

不会导致类初始化的情况 :

  • 类加载器的 loadClass 方法 :加载阶段
  • Class.forName(String,false) :加载阶段
  • 创建该类的数组不会触发初始化 :加载阶段
  • 访问 类.class :加载阶段
  • 访问类的 静态常量(基本类型和字符串) :链接-准备阶段

验证:导致类初始化的情况

public class Init {
    static {
        //1.main方法所在类。首先被初始化
        System.out.println("1.init:main方法所在类");
    }
    public static void main(String[] args) throws ClassNotFoundException {
        //2.new
        A newTest=new A();
        //3.子类初始化
        //4.访问类的 静态变量、静态方法
        int a=B.b;
        //5.Class.forName(String)
        Class.forName("org.example.classLoading.link.D");
    }
}
class A{
    static {
        System.out.println("2.init:new的类");
    }
}
class B extends C{
    static int b=1;
    static {
        System.out.println("4.init:访问类的静态变量、静态方法");
    }
}
class C{
    static {
        System.out.println("3.init:子类初始化");
    }
}
class D{
    static {
        System.out.println("5.init:Class.forName(String)");
    }
}
1.init:main方法所在类
2.init:new的类
3.init:子类初始化
4.init:访问类的静态变量、静态方法
5.init:Class.forName(String)

验证:不会导致类初始化的情况

public class Init {
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader classLoader = Init.class.getClassLoader();
        //1.类加载器的 loadClass 方法 :加载阶段
        classLoader.loadClass("org.example.classLoading.link.A");
        //2.Class.forName(String,false) :加载阶段
        Class.forName("org.example.classLoading.link.A",false,classLoader);
        //3.创建该类的数组不会触发初始化 :加载阶段
        A[] arr=new A[2];
        //4.访问 类.class :加载阶段
        Class<A> clazz = A.class;
        //5.访问类的 静态常量(基本类型和字符串) :链接-准备阶段
        int a=A.a;
    }
}
class A{
    static final int a=1;
    static {
        System.out.println("init A");
    }
}
Process finished with exit code 0

你可能感兴趣的:(#,JVM相关,jvm,java,开发语言)