浅析:类的加载过程

浅析:类的加载过程,第1张

类的加载过程有哪几个步骤?

加载——>验证——>准备——>解析——>初始化——>使用——>卸载

示例代码:

public class Magic {
    private static Magic instance = new Magic();
    private static  int count = 1;
    
    private Magic(){
        System.out.println(count);
    }
    
    public static Magic getInstance() {
        return instance;
    }
    
    public static void main(String[] args) {
        Magic.getInstance();
    }
}

首先JVM的ClassLoader将Magic.class文件加载到虚拟机内存中,也就是我们说的JVM中,确保被加载类的正确性和安全性后,JVM会给类的静态变量分配内存,并为其赋默认值。
而后将类中的符号引用转换成直接引用,再为静态变量初始化正确的值。
也许上面的过程还是不够清晰,那么我们来通过断点调试看看实际的现象。

PS:

JVM虚拟机规范并没有对类的加载时机做出严格的要求,只规定了以下五种情况需要立刻触发类的初始化:

遇到new,getstatic,putstatic和invokestatic这四个字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。

使用反射机制对类进行调用的时候,如果类没有进行过初始化,则需要先触发其初始化。

当初始化一个类时,如果其父类还没有进行过初始化,则需要先触发其父类的初始化。

虚拟机启动时,用户需要指定一个要执行的主类(包含main方法),此时会先初始化这个类

使用JDK1.7的动态语言支持时,如果一个MethodHandle实例最后的解析结果包含REF_getStatic,REF_putStatic,REF_invokeStatic的方法句柄,且这个方法句柄对应的类没有初始化,则需要先对其进行初始化。

其余条件下,可以由JVM虚拟机自行决定何时去加载一个类。

加载(Loading)

加载过程是Java的一大特点,类的来源可以多种多样,压缩包、网络字节流、运行时动态计算生成(reflect)等等…这也造就了Java语言强大的动态特性。
通过一个类的完整限定名来获取定义此类的二进制字节流(注意,字节流的来源非常灵活)
将这个字节流所代表的静态储存结构转换成为方法区的运行时数据结构
在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

验证(Verification)

这一过程主要是为了确保Class的字节流中包含的信息符合虚拟机标准,以免造成破坏
文件格式验证、元数据验证、字节码验证,通过数据流和控制流分析确定程序的语义是合法的 符号引用验证,确保解析动作能够正常执行

准备(Preparation)

这一阶段将会为类变量分配内存并设置其初始值,注意此时进行内存分配的仅包括类变量(static修饰),并且初始值通常情况下是数据类型的零值而不是设定值

解析(Resolution)

此阶段虚拟机将常量池内的符号替换为直接引用,主要包含以下动作:类或接口的解析、字段解析、类方法解析、接口方法解析

初始化(Initialization)

这时类加载过程的最后一步,这部分开始真正的执行Java代码,也就是说,这个阶段可以由程序员参与。

调试

在第8行处打上断点,debug运行发现此时并没有执行第9行的代码,但是Variables中却有名为count的静态变量并且值为0;

接下来我们跟进Magic的构造方法中,打印了count的值

为什么是0呢?

这是因为前面我们说的类加载过程中的准备步骤,count是被static修饰的静态变量,所以在准备阶段,静态变量都会被放到分配好的方法区中,并且赋默认值。

private static Magic instance = new Magic();

而这里是一个典型的单例模式(饿汉式),会在执行main方法前调用构造方法中的打印。这也就很明了的解释了结果为什么是0了。

现在我们将这个静态变量改成final修饰的常量,打印的值是如何?

被final修饰的常量虽然也是和静态变量一样存放在JVM的方法区中,但二者的区别就是一个是先在准备阶段创建赋默认值,再在初始化阶段赋正确的值。另一个是在创建时就赋正确的值,并且不会被修改。

总结

通过简单的例子可以很好地理解类在加载过程中对静态变量和常量的创建和初始化,但需要结合JVM内存结构深入理解。

参考:Java类加载、字节码最强总结攻略

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/794635.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-06
下一篇 2022-05-06

发表评论

登录后才能评论

评论列表(0条)

保存