java懒加载的原理_《Java 底层原理》Jvm 类的加载原理

前言

一直想好好的了解一下JVM,这次就来一起了解一下JVM是如何实现类的加载过程的。

原理

类加载的生命周期

d342d4ef67919fbaa5d312f5c27c54ad.png

1. 加载

5种类加载情况:

在遇到 new、putstatic、getstatic、invokestatic 字节码指令时,如果类尚未初始化,则需要先触发初始化。

对类进行反射调用时,如果类还没有初始化,则需要先触发初始化。

初始化一个类时,如果其父类还没有初始化,则需要先初始化父类。

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

当使用 JDK 1.7 的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果为 REF_getStatic、REF_putStatic、REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类还没初始化,则需要先触发初始化。

因为Java类加载是不关心这个文件来自哪里?

所有Java的对象可以来自Jar,war,网络,CGlib,数据库等等。

使用到对象才会加载。

2. 验证

文件格式验证

元数据验证

字节码验证

符号引用验证

3. 准备

为静态变量分配内存、为基础数据类型赋初值,

08018810366e66d6981677bf7dfc460a.png

如果被final修饰,在编译的时候会给属性添加ConstantValue属性,准备阶段直接完成赋值操作,无需赋初值。

4. 解析

间接引用转直接引用。

间接引用:指向常量池。

直接引用:指向内存地址。

5. 初始化

执行静态代码段(静态代码块,静态变量):clinit

测试代码写了一个静态变量b 通过字节码文件也能清楚的看到信息。

9b779a7d47dc1b4b620e9c657f09191c.png

静态代码的执行顺序和定义的顺序一致,改点可以通过案例说明。

public classJvmTest1 {public static voidmain(String[] args) {

Test1 test1=Test1.getTest();

System.out.println(Test1.val1);

System.out.println(Test1.val2);

}

}classTest1{public static intval1 ;

Test1(){

val1++;

val2++;

}public static Test1 instance = newTest1();public static int val2 = 1 ; //该代码放在在初始化代码

public staticTest1 getTest(){returninstance;

}

}

运行结果:

0e592e85c20a65057482a03b8a5e9663.png

运行结果是1和1 ,造成这个结果的原因就是静态代码的顺序执行,先创建了对象,对象中构造函数执行了, 后面再执行 public static int val2 = 1 ,值就被覆盖回去了。

6. 使用

对象已经初始化完成,这个对象就可以再其他地方被使用。

7. 卸载

销毁之前加载的对象信息。

Jvm里面有一个类状态的枚举:

9f46a829b9dd19cbdc9b07369b817213.png

JVM类加载细节

1. jvm类加载会加锁,防止类加载出现资源争用的情况。

通过一个案例证明一下:

public classJvmTest3 {public static voidmain(String[] args) {new Thread(() ->{while(true){newAA();

}

}).start();new Thread(() ->{while(true) {newBB();

}

}).start();

}

}classAA{static{

System.out.println("创建AA对象");newBB();

}

}classBB{static{

System.out.println("创建BB对象");newAA();

}

}

运行结果:

906cc1bb20970a84e898407e01fa451d.png

执行被锁,无法继续执行。

2. Jvm加载对象属于懒加载(对象没有使用的时候,不会去加载该对象),下面通过案例说明:

public classJvmTest2 {public static voidmain(String[] args) {

System.out.println(B.str);

}

}classA {static String str = "a";static{

System.out.println("static a");

}

}class B extendsA {static{

System.out.println("static b");

}

}

运行结果:

83c0a59a4745388a29c80dc3fa6e1e43.png

可以明显的看到B的static方法没有被执行,因为这段逻辑中,不需要是到B这个对象,哪怕B对象是A对象的子类。

总结

Jvm 类加载是核心逻辑之一,非常重要,对一个变成人员的提升也是非常的有帮助。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值