Java虚拟机之类加载

论坛 期权论坛 脚本     
niminba   2021-5-23 02:58   1098   0

一、类加载流程

类加载的流程可以简单分为三步:

  • 加载
  • 连接
  • 初始化

而其中的连接又可以细分为三步:

  • 验证
  • 准备
  • 解析

下面会分别对各个流程进行介绍。

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类的实例,表示该类型

第一步获取二进制数据流,途径有很多,包括:

  • 字节码文件
  • JAR/ZIP压缩包
  • 从网络加载

等等,获取到二进制数据流后,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[[[O O.ykkke`yk.by. 9.*O OdO[ O(j9g*9kel9.+y`c:eo#:/+*OXY Oy#9g*:!.bykd9.+ycz oBOXY\[\[[YJH\^[ Oo. 9.*/&g*O\ O.+z(*;.!.byo`.BOXY\[Y\[[YJO Oko. 9.*m:/oy B 9b:/oyfj9b! σBg*9!O]O On#OO O/&b&b:/oyfj9..m9.*n9n{b!b*& B[BOd+noyfj;O\\\ O OBOjyleyb:/oyfj;O^[\\ O OBOn9b:/oyfj;.gnoyfj;O\\\ O OB Bc%g*9n.bz!9b:/oyfj;.."`(y9i."B[OH^ X[Y\[Y[Hg*:/iyfaZZ[Z[[^][K NKL LXMLYXL  B. :"+9iz+!9.*b:/oyfj:-':# ."B[BOd+noyfj;-':/oy.9onO \ Oc!y.+y OBOjyleyb:/oyfj;-':/oOOX \ O."nOBOn9b:/oyfj;-':/oy-nOBO!.byb:/oyfj;b:/oy. 9.`%9o9. :"+9+-nOB Bci/ 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/&!:/ox B[OH^ X[Y\[Y[Hg*:/iyfaZZ[Z[[^][K NKL LXMLYXL  Bg*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]O O+ 9kg*O]O O.+yy"ykhy.#.b;.9on++noyfj:/c9b:/oyhc#9i9l'z+*9n+noyfj;o9`/9+O[ O B]\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.
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1060120
帖子:212021
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP