JVM -- 类加载

Posted MinggeQingchun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 工具查看

二、链接

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

1、static 变量在 JDK 7 之前存储于 instanceKlass 末尾,从 JDK 7 开始,存储于 _java_mirror 末尾

2、static 变量分配空间和赋值是两个步骤,分配空间在准备阶段完成,赋值在初始化阶段完成

3、如果 static 变量是 final 的基本类型,以及字符串常量,那么编译阶段值就确定了,赋值在准备阶 段完成

4、如果 static 变量是 final 的,但属于引用类型,那么赋值也会在初始化阶段完成

public class Load1 
    public static void main(String[] args) throws ClassNotFoundException, IOException 
        ClassLoader classloader = Load1.class.getClassLoader();
        Class<?> c = classloader.loadClass("com.mycompany.load.C");

        //new C();
        System.in.read();
    


class C 
    D d = new D();


class D 

loadClass() 方法

public static void main(java.lang.String[]) throws java.lang.ClassNotFoundException, java.io.IOException;
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #2                  // class com/mycompany/load/Load1
         2: invokevirtual #3                  // Method java/lang/Class.getClassLoader:()Ljava/lang/ClassLoader;
         5: astore_1
         6: aload_1
         7: ldc           #4                  // String com.mycompany.load.C
         9: invokevirtual #5                  // Method java/lang/ClassLoader.loadClass:(Ljava/lang/String;)Ljava/lang/Class;
        12: astore_2
        13: getstatic     #6                  // Field java/lang/System.in:Ljava/io/InputStream;
        16: invokevirtual #7                  // Method java/io/InputStream.read:()I
        19: pop
        20: return
      LineNumberTable:
        line 7: 0
        line 8: 6
        line 11: 13
        line 12: 20
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      21     0  args   [Ljava/lang/String;
            6      15     1 classloader   Ljava/lang/ClassLoader;
           13       8     2     c   Ljava/lang/Class;
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
           13       8     2     c   Ljava/lang/Class<*>;
    Exceptions:
      throws java.lang.ClassNotFoundException, java.io.IOException

new C();

public static void main(java.lang.String[]) throws java.lang.ClassNotFoundException, java.io.IOException;
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: new           #2                  // class com/mycompany/load/C
         3: dup
         4: invokespecial #3                  // Method com/mycompany/load/C."<init>":()V
         7: pop
         8: getstatic     #4                  // Field java/lang/System.in:Ljava/io/InputStream;
        11: invokevirtual #5                  // Method java/io/InputStream.read:()I
        14: pop
        15: return
      LineNumberTable:
        line 10: 0
        line 11: 8
        line 12: 15
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      16     0  args   [Ljava/lang/String;
    Exceptions:
      throws java.lang.ClassNotFoundException, java.io.IOException

三、初始化

<cinit>()V 方法

初始化即调用<cinit>()V ,虚拟机会保证这个类的『构造方法』的线程安全

调用时机

1、类初始化是【懒惰的】

1、main 方法所在的类,总会被首先初始化

2、首次访问这个类的静态变量或静态方法时

3、子类初始化,如果父类还没初始化,会引发

4、子类访问父类的静态变量,只会触发父类的初始化

5、Class.forName

6、new 会导致初始化

2、不会导致类初始化的情况

1、访问类的 static final 静态常量(基本类型和字符串)不会触发初始化

2、类对象.class 不会触发初始化

3、创建该类的数组不会触发初始化

4、类加载器的 loadClass 方法

5、Class.forName 的参数 2 为 false 时

public class Load2 
    static 
        System.out.println("main init");
    

    public static void main(String[] args) throws ClassNotFoundException, IOException 
        //System.out.println("========不会导致类初始化的情况========");
        // 1. 静态常量不会触发初始化
        //System.out.println(B.b);
        //main init
        //5.0

        // 2. 类对象.class 不会触发初始化
        //System.out.println(B.class);
        //main init
        //class com.mycompany.load.B

        // 3. 创建该类的数组不会触发初始化
        //System.out.println(new B[0]);
        //main init
        //[Lcom.mycompany.load.B;@511d50c0

        // 4. 不会初始化类 B,但会加载 B、A
//        ClassLoader cl = Thread.currentThread().getContextClassLoader();
//        cl.loadClass("com.mycompany.load.B");
        //main init

        // 5. 不会初始化类 B,但会加载 B、A
//        ClassLoader c2 = Thread.currentThread().getContextClassLoader();
//        Class.forName("com.mycompany.load.B", false, c2);
        //main init

        System.out.println("========类初始化========");

        // 1. 首次访问这个类的静态变量或静态方法时
        //System.out.println(A.a);
        //main init
        //a init
        //0

        // 2. 子类初始化,如果父类还没初始化,会引发
        //System.out.println(B.c);
        //main init
        //a init
        //b init
        //false

        // 3. 子类访问父类静态变量,只触发父类初始化
        //System.out.println(B.a);
        //main init
        //a init
        //0

        // 4. 会初始化类 B,并先初始化类 A
        Class.forName("com.mycompany.load.B");
        //main init
        //a init
        //b init
    


class A 
    static int a = 0;
    static 
        System.out.println("a init");
    


class B extends A 
    final static double b = 5.0;
    static boolean c = false;
    static 
        System.out.println("b init");
    

以上是关于JVM -- 类加载的主要内容,如果未能解决你的问题,请参考以下文章

JVM 类加载

JVM : 2 JVM类加载机制

JVM类加载器

JVM类加载器

jvm类加载器,类加载机制详解

jvm类加载器,类加载机制详解