类加载过程小结

439 阅读2分钟

总结一下类加载过程,并写一些demo

类加载的过程包括: 加载-连接(验证,准备,解析)-初始化

  • 加载:Loader 通过二进制码生成一个 Class 对象的过程。会把字节码转储为方法区运行时的数据结构
  • 验证:是为了确保Class文件的数据符合虚拟机规范,其中包括:文件格式、元数据验证(保证符合java语义),字节码验证(保证符合),符号引用验证
  • 准备:为类变量分配内存空间并且设置初始值的过程(static int value)。
  • 解析:虚拟机把常量池内的符号引用替换成直接引用的过程。
  • 初始化:执行类构造器的过程,这一部分会更加接近程序员开发

Demo

  • 初始化和简单的解析过程
public class InitSort {

    static class Parent {
        public static int A = 1;
        static {
            System.out.println("parant: A:" + A);
            A = 2;
        }
    }

    static class Sub extends Parent {
        public static final int C = 10;     // 解析阶段完成符号变量->直接引用的设置
        public static int B = A;            // 初始化阶段会先于 static {} 模块执行
        static {
            System.out.println("Sub B:" + B);
            B = 3;
        }
    }

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        // 使用 static final int 类不会触发初始化,这是解析阶段就已经完成的。
        System.out.println("不会触发初始化的:Sub.C:" + Sub.C);
        // loadClass 也不会触发初始化,解析
        Class<?> clazz = InitSort.class.getClassLoader().loadClass("learn.macro.mall.jvm.classloader.InitSort$Sub");
        // forName 会触发初始化,这是 jdbc 触发 mysql 连接的一种方式,内部使用static代码块进行处理
        InitSort.class.forName("learn.macro.mall.jvm.classloader.InitSort$Sub");
    }
}

输出:

不会触发初始化的:Sub.C:10
parant: A:1
Sub B:2

观察一个字节码的常量池

java git:(master) ✗ javap -verbose learn/macro/mall/jvm/classloader/ByteCodeSample.class
Classfile /Users/qpm/work/github/mall/mall-demo/src/main/java/learn/macro/mall/jvm/classloader/ByteCodeSample.class
  Last modified 2020-4-3; size 291 bytes
  MD5 checksum 5f7f9cd63aa238e2d1ae6d95c5884789
  Compiled from "ByteCodeSample.java"
public class learn.macro.mall.jvm.classloader.ByteCodeSample
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:      // 这里就是常量池,class文件内,常量池是一个符号引用,解析阶段会将这些符号应用改成直接引用,让JVM可以直接定位到方法、变量的内存位置。
   #1 = Methodref          #3.#12         // java/lang/Object."<init>":()V
   #2 = Class              #13            // learn/macro/mall/jvm/classloader/ByteCodeSample
   #3 = Class              #14            // java/lang/Object
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               add
   #9 = Utf8               (II)I
  #10 = Utf8               SourceFile
  #11 = Utf8               ByteCodeSample.java
  #12 = NameAndType        #4:#5          // "<init>":()V
  #13 = Utf8               learn/macro/mall/jvm/classloader/ByteCodeSample
  #14 = Utf8               java/lang/Object