JVM类加载与双亲委派机制被打破

本文详细介绍了JVM的类加载过程,包括加载、验证、准备、解析和初始化阶段。强调了类加载的时机,如主动引用和被动引用的区别。此外,还讨论了双亲委派机制,以及在JNDI服务、SPI和服务提供者加载等场景下如何打破这一机制。最后,文章提到了类加载的动态性和热部署需求对双亲委派模型的影响。

前言

前文已经讲了虚拟机将java文件编译成class文件后的格式

java文件经过编译,形成class文件,那么虚拟机如何将这些Class文件读取到内存中呢?

加载的时机

JVM 会在程序第一次主动引用类的时候加载该类,被动引用时并不会引发类加载的操作。也就是说,JVM 并不是在一开始就把一个程序就所有的类都加载到内存中,而是到不得不用的时候才把它加载进来,而且只加载一次。

一个类的生命周期如图所示:

上图中的加载、验证、准备、初始化、卸载这几个步骤是相对固定的,但是初始化这一步不一定,他在某些情况下可以是再初始化之后执行。

 

加载

加载是类加载的第一阶段,虚拟机此时主要做以下三件事情:

1.通过类的全限定名来获取定义这个类的二进制字节流。

2.将字节流的静态存储结构转化为运行时的数据结构;

3.在内存中生成该类的 java.lang.Class 对象,作为方法区这个类各种数据访问入口。

主动引用一定会加载,但是被动引用则不一定

主动引用

  1. 遇到 new、getstatic、putstatic、invokestatic 字节码指令,例如:

    使用 new 实例化对象;

    读取或设置一个类的 static 字段(被 final 修饰的除外);

    调用类的静态方法。

  2. 对类进行反射调用;

  3. 初始化一个类时,其父类还没初始化(需先初始化父类);

    这点类与接口具有不同的表现,接口初始化时,不要求其父接口完成初始化,只有真正使用父接口时才初始化,如引用父接口中定义的常量。

  4. 虚拟机启动,先初始化包含 main() 函数的主类;

  5. JDK 1.7 动态语言支持:一个 java.lang.invoke.MethodHandle 的解析结果为 REF_getStatic、REF_putStatic、REF_invokeStatic。

被动引用

  1. 通过子类引用父类静态字段,不会导致子类初始化;

  2. Array[] arr = new Array[10]; 不会触发 Array 类初始化;

  3. static final VAR 在编译阶段会存入调用类的常量池,通过 ClassName.VAR 引用不会触发 ClassName 初始化。

也就是说,只有发生主动引用所列出的 5 种情况,一个类才会被加载到内存中,也就是说类的加载是 lazy-load 的,不到必要时刻是不会提前加载的,毕竟如果将程序运行中永远用不到的类加载进内存,会占用方法区中的内存,浪费系统资源。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值