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 -- 类加载的主要内容,如果未能解决你的问题,请参考以下文章