一、类加载流程
类加载的流程可以简单分为三步:
而其中的连接又可以细分为三步:
下面会分别对各个流程进行介绍。
1.1 类加载条件
在了解类接在流程之前,先来看一下触发类加载的条件。
JVM 不会无条件加载类,只有在一个类或接口在初次使用的时候,必须进行初始化。这里的使用是指主动使用,主动使用包括如下情况:
- 创建一个类的实例的时候:比如使用
new 创建,或者使用反射、克隆、反序列化
- 调用类的静态方法的时候:比如使用
invokestatic 指令
- 使用类或接口的静态字段:比如使用
getstatic /putstatic 指令
- 使用
java.lang.reflect 中的反射类方法时
- 初始化子类时,要求先初始化父类
- 含有
main() 方法的类
除了以上情况外,其他情况属于被动使用,不会引起类的初始化。
比如下面的例子:
public class Main {
public static void main(String[] args){
System.out.println(Child.v);
}
}
class Parent{
static{
System.out.println("Parent init");
}
public static int v = 100;
}
class Child extends Parent{
static {
System.out.println("Child init");
}
}
输出如下:
Parent init
100
而加上类加载参数-XX:+TraceClassLoading 后,可以看到Child 确实被加载了:
[0.068s][info ][class,load] com.company.Main
[0.069s][info ][class,load] com.company.Parent
[0.069s][info ][class,load] com.company.Child
Parent init
100
但是并没有进行初始化。另外一个例子是关于final 的,代码如下:
public class Main {
public static void main(String[] args){
System.out.println(Test.STR);
}
}
class Test{
static{
System.out.println("Test init");
}
public static final String STR = "Hello";
}
输出如下:
[0.066s][info ][class,load] com.company.Main
Hello
Test 类根本没有被加载,因为final 被做了优化,编译后的Main.class 中,并没有引用Test 类:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4 // String Hello
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
在字节码偏移3的位置,通过ldc 将常量池第4项入栈,此时在字节码文件中常量池第4项为:
#3 = Class #24 // com/company/Test
#4 = String #25 // Hello
#5 = Methodref #26.#27 // java/io/PrintStream.println:(Ljava/lang/String;)V
因此并没有对Test 类进行加载,只是直接引用常量池中的常量,因此输出没有Test 的加载日志。
1.2 加载
类加载的时候,JVM 必须完成以下操作:
- 通过类的全名获取二进制数据流
- 解析类的二进制数据流为方法区内的数据结构
- 创建
java.lang.Class 类的实例,表示该类型
第一步获取二进制数据流,途径有很多,包括:
等等,获取到二进制数据流后,JVM 进行处理并转化为一个java.lang.Class 实例。
1.3 验证
验证的操作是确保加载的字节码是合法、合理并且规范的。步骤简略如下:
- 格式检查:判断二进制数据是否符合格式要求和规范,比如是否以魔数开头,主版本号和小版本号是否在当前
JVM 支持范围内等等
- 语义检查:比如是否所有类都有父类存在,一些被定义为
final 的方法或类是否被重载了或者继承了,是否存在不兼容方法等等
- 字节码验证:会试图通过对字节码流的分析,判断字节码是否可以正确被执行,比如是否会跳转到一条不存在的指令,函数调用是否传递了正确的参数等等,但是却无法100%判断一段字节码是否可以i."BOXX\\[[YJH\^[Ok. 9.*d#{b:/oy. 9.*/*O\Ok/o.#yb,9 n.BOXY[[\Y[P\]V[[[OO.ykkke`yk.by. 9.*OOdO[O(j9g*9kel9.+y`c:eo#:/+*OXYOy#9g*:!.bykd9.+ycz oBOXY\[\[[YJH\^[Oo. 9.*/&g*O\O.+z(*;.!.byo`.BOXY\[Y\[[YJOOko. 9.*m:/oyB9b:/oyfj9b!σBg*9!O]OOn#OOO/&b&b:/oyfj9..m9.*n9n{b!b*&B[BOd+noyfj;O\\\OOBOjyleyb:/oyfj;O^[\\OOBOn9b:/oyfj;.gnoyfj;O\\\OOBBc%g*9n.bz!9b:/oyfj;.."`(y9i."B[OH^X[Y\[Y[Hg*:/iyfaZZ[Z[[^][KNKLLXMLYXLB. :"+9iz+!9.*b:/oyfj:-':# ."B[BOd+noyfj;-':/oy.9onO\Oc!y.+yOBOjyleyb:/oyfj;-':/oOOX\O."nOBOn9b:/oyfj;-':/oy-nOBO!.byb:/oyfj;b:/oy. 9.`%9o9. :"+9+-nOBBci/Bn9y."noy/oi/b:/oy(yo#miz+#9l,y+*9b:/oy&{/&b)9yodn)mo{i9mo{`z/:/oyi9y"{/&ab:+`cb:/o{c.g"y. 9-`yab9b)9y+:/o{i9y"yg*9i9if9cb:/o{i9cb:/oyi,z-){b&y/&!:/oxB[OH^X[Y\[Y[Hg*:/iyfaZZ[Z[[^][KNKLLXMLYXLBg*9."f+{n9b:/oyfj9c..jyleyb:/oyfj;jyleyb:/oyfj9c..d+noyfj;od :)yb:/oy. 9.*&{/&ab9.yl`b:/oyfj9o 9ic9b)9{odyb:/oy&y/&.ml`o 9i:/o{/y(yd$y.",9b:/oy$9bBg*9`9"yb:/oyfj9.+{d+noyfj9+nyb*#9nm.#y+*O]OO+ 9kg*O]OO.+yy"ykhy.#.b;.9on++noyfj:/c9b:/oyhc#9i9l'z+*9n+noyfj;o9`/9+O[OB]\HHBH\H\]NB[K[[\\\
HOH[
NOB]B/9..Bb,9i:/l]z&f..b:/oy,y.,:/9&alyb:/oyaykz+'9c..cy%c"9al#9&.#i&i&+/c.
|