【深入理解JVM】:类加载机制

本文深入探讨了Java虚拟机(JVM)的类加载机制,包括类加载的时机、过程,以及验证、准备、解析和初始化等阶段。详细阐述了加载过程中主动引用与被动引用的区别,以及类加载过程中的字节码验证、符号引用验证等安全措施。此外,还介绍了类的生命周期和初始化的执行顺序,强调了多线程环境下类初始化的同步控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。

与那些在编译时需要进行链接工作的语言不同,在Java语言里,类型的加载、连接和初始化过程都是在程序运行期间完成的,例如import java.util.*下面包含很多类,但是,在程序运行的时候,虚拟机只会加载哪些我们程序需要的类。这种策略虽然会令类加载时稍微增加一些性能开销,但是会为Java应用程序提供高度的灵活性。

类加载的时机

类从创建起(这里的类也可能是接口,下同),就注定了其是有生命周期的(这里的生命周期指的是类在运行期间所经历的过程,与是否存储在存储介质上无关)。类从被虚拟机加载到内存中开始,到卸载出内存为止,它的生命周期经历了加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading),一共七个阶段,其中验证、准备、解析部分统称为连接。这七个阶段可以用如下图描述:

类的生命周期

从上图中可以明显看出各个阶段是有顺序的,加载、验证、准备、初始化这个5个阶段的顺序是固定的,也就是说类的加载过程必须按照这种顺序按部就班开始;解析阶段则不一定,解析阶段的工作完全可能在初始化之后才开始,之所以这么设计,就是为了支持Java语言的动态绑定。还有一点需要注意的是,虽然上述的5个阶段可能按照顺序开始,但是并不是说一个接一个阶段完成后才开始,一个阶段的进行完全可能激活另一个阶段的进行,交叉混合式的进行

那么什么情况下需要开始类加载过程的第一个阶段,加载到内存中呢?这就不得不涉及两个概念:主动引用和被动引用。根据Java虚拟机的规范,只有5中情况属于主动引用:

  1. 遇到new(使用new 关键字实例化一个对象)、getstatic(读取一个类的静态字段)、putstatic或者invokestatic(设置一个类的静态字段)这4条指令的时候,如果累没有进行过初始化。则需要先触发其初始化。

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

  3. 当初始化一个类的时候,如果其父类没有初始化,则需要先触发其父类的初始化

  4. 程序启动需要触发main方法的时候,虚拟机会先触发这个类的初始化

  5. 当使用jdk1.7的动态语言支持的时候,如果一个java.lang.invoke.MethodHandler实例最后的解析结果为REF_getStatic、REF_pusStatic、REF_invokeStatic的方法句柄(句柄中包含了对象的实例数据和类型数据,句柄是访问对象的一种方法。句柄存储在堆中),并且句柄对应的类没有被初始化,那么需要先触发这个类的初始化。

5种之外情况就是被动引用。被动引用的经典例子有:

  1. 通过子类引用父类的静态字段
    这种情况不会导致子类的初始化,因为对于静态字段,只有直接定义静态字段的类才会被触发初始化,子类不是定义这个静态字段的类,自然不能被实例化。

  2. 通过数组定义来引用类,不会触发该类的初始化
    例如, Clazz[] arr = new Clazz[10];并不会触发。

  3. 常量不会触发定义常量的类的初始化
    因为常量在编译阶段会存入调用常量的类的常量池中,本质上并没有引用定义这个常量的类,所以不会触发定义这个常量的类的初始化。

对于这5种主动引用会触发类进行初始化的场景,在java虚拟机规范中限定了“有且只有”这5种场景会触发类的加载。

类加载的过程

加载

在加载阶段虚拟机需要完成以下三件事:

  1. 通过一个类的全限定名称来获取此类的二进制字节流

  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构

  3. 在内存中生成一个代表这个类的java.lang.Clas

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值