news 2026/6/16 13:16:49

JVM的类加载机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JVM的类加载机制

JVM的类加载机制是Java“一次编写,到处运行”和动态性的基石。它的核心任务就是:找到并验证字节码文件(.class),将其定义成JVM能直接使用的Java类

简单来说,这个过程由三大部分组成:加载、连接、初始化。连接又可以细分为验证、准备、解析。


一、类加载器的体系结构(双亲委派模型)

这是JVM类加载机制最核心的设计。JVM不会用一个“万能加载器”,而是使用一组有层次关系的加载器。

启动类加载器 (Bootstrap ClassLoader) ↑ (父加载器,实际是C++实现,Java中为null) 扩展类加载器 (Extension ClassLoader) ↑ (父加载器为 Bootstrap) 应用程序类加载器 (Application ClassLoader) ↑ (父加载器为 Extension) 自定义类加载器 (Custom ClassLoader)
  • 启动类加载器:加载JAVA_HOME/lib目录下的核心类库,比如rt.jar中的java.lang.*。它是最顶层的,没有父加载器。

  • 扩展类加载器:加载JAVA_HOME/lib/ext目录下的扩展类库。

  • 应用程序类加载器:加载用户类路径(CLASSPATH)下的类,也就是你编写的.class文件。

  • 自定义类加载器:开发者可以继承ClassLoader类,实现自定义的加载逻辑,比如从网络、数据库或加密文件中加载类。

双亲委派模型的工作流程

当一个类加载器(比如应用程序类加载器)收到加载一个类的请求时,它不会自己立即去加载,而是:

  1. 向上委派:先将请求委派给它的父加载器(扩展类加载器)。

  2. 递归检查:父加载器同样会继续向上委派给自己的父加载器(启动类加载器),直到最顶层的启动类加载器。

  3. 尝试加载:每个父加载器尝试在自己的加载路径中查找并加载这个类。

  4. 向下查找:如果所有父加载器都无法加载,才会回到最初发起请求的子加载器,让它自己去类路径里查找并加载。

这样做的好处

  • 避免重复加载:父加载器加载过的类,子加载器不会再次加载。

  • 保证核心类库的安全:你无法自己写一个java.lang.Object类去替换系统的。因为当请求加载java.lang.Object时,双亲委派会一直向上委派给启动类加载器,它找到并加载了正确的Object后,应用程序类加载器就不会再加载你写的“假”类了。


二、类的生命周期:加载、连接、初始化

一个类从被加载到内存,到卸载出内存,完整的生命周期包括7个阶段。其中,加载、验证、准备、初始化、卸载这5个阶段的顺序是确定的。

1. 加载(Loading)

JVM要做三件事:

  • 通过一个类的全限定名(如java.lang.String)获取其二进制字节流(来源可以是.class文件、jar包、网络、甚至动态生成)。

  • 将这个字节流所代表的静态存储结构转化为方法区(元空间)的运行时数据结构

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

加载是类加载器工作的主要阶段,并且可以结合“双亲委派模型”来理解。

2. 连接(Linking)

分为三步,其中解析阶段有时可能在初始化之后才开始。

  • 验证(Verification):确保.class字节流符合JVM规范,不会危害JVM安全。检查项很多:文件格式、元数据、字节码、符号引用等。这是类加载机制安全防护的第一关。

  • 准备(Preparation):为类变量(static修饰的变量)在方法区中分配内存,并设置默认初始值(零值)。

    • public static int value = 123;:在准备阶段后,value的值是0,而不是123123是在后面的初始化阶段才赋值的。

    • 如果变量是static final(常量),情况不同。例如public static final int value = 123;,编译器会在准备阶段就直接给value赋值为123

  • 解析(Resolution):将常量池内的符号引用替换为直接引用

    • 符号引用:字面上的引用,例如一个类中调用了另一个类的method(),在字节码里就是"com/example/OtherClass.method"这样一串文字。

    • 直接引用:指向目标的内存地址,或者相对于方法区某个位置的偏移量。一旦找到具体的内存地址,后面的调用就是真正的内存访问了。

3. 初始化(Initialization)

这是类加载过程的最后一步。JVM会执行类构造器<clinit>()方法,为类变量赋程序员定义的初始值,并执行静态代码块。

  • <clinit>()方法由编译器自动收集类中所有类变量的赋值动作静态语句块中的语句合并产生。

  • 对于public static int value = 123;:在初始化阶段,value才会真正被赋值为123

JVM 保证在首次主动使用一个类时才会初始化它。主动使用的情况包括:

  • new一个对象

  • 访问或调用一个类的静态方法/静态字段(final常量除外)

  • 反射调用

  • 初始化一个类时,其父类尚未初始化,则先初始化父类

  • 主类(包含main方法的类)


一个完整的例子

假设运行new MyApp()

  1. 加载:应用程序类加载器收到请求,向上委派,最终由应用程序类加载器自己找到MyApp.class,加载成Class对象。

  2. 连接

    • 验证:检查MyApp.class格式是否正确。

    • 准备:为MyApp的静态变量分配内存并设零值(比如static int count = 10;此时count=0)。

    • 解析:将MyApp内部引用的System.out等符号,解析成真正的内存地址。

  3. 初始化:执行静态代码块和静态变量赋值,count被赋值为10。如果父类未初始化,先递归初始化父类。

  4. 完成!现在可以安全地创建MyApp实例了。

理解类加载机制,是排查ClassNotFoundExceptionNoClassDefFoundError,以及实现热部署、字节码增强等技术的基础。如果你还想继续了解类的卸载条件,或者自定义类加载器如何实现,我可以进一步为你讲解。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/12 8:42:49

SNMPv1/v2c-从报文解析到实战配置:一次搞懂核心机制

1. SNMP协议基础&#xff1a;从网络管理需求到协议选型 网络管理就像城市的交通监控系统&#xff0c;需要实时掌握每台设备的运行状态。SNMP&#xff08;Simple Network Management Protocol&#xff09;就是这套监控系统的"通用语言"&#xff0c;让管理员能够与成千…

作者头像 李华
网站建设 2026/6/11 1:43:40

Matlab版GA-BP图像分割工具:含预置模型、测试图与端到端训练脚本

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;直接运行main.m就能完成图像分割任务的Matlab工具包&#xff0c;用遗传算法自动调优BP神经网络的结构参数和连接权值&#xff0c;不依赖Deep Learning Toolbox等额外工具箱&#xff0c;R2015b及以上版本开箱即用…

作者头像 李华
网站建设 2026/6/11 12:49:28

进化算法工程落地手册:从失效现场到稳准快优化

1. 项目概述&#xff1a;这不是“进化算法”的科普&#xff0c;而是一次真实落地的工程实践复盘你点开这篇文章&#xff0c;大概率不是想听“进化算法&#xff08;Evolutionary Algorithms, EAs&#xff09;”的定义——毕竟维基百科两句话就能说清&#xff1a;它是一类受生物进…

作者头像 李华
网站建设 2026/6/11 16:16:48

SAP 角色管理工具初始化,从 SU25 到 PFCG 的一条完整授权主线

我们今天在做 SAP 权限整理时,经常会碰到一个看似很小、实际影响很大的动作,执行 SU25。很多项目上把它当成 Basis 初始化步骤,做完就过去了,后面角色顾问继续在 PFCG 里建角色,ABAP 开发继续写 AUTHORITY-CHECK,Fiori 团队继续配 catalog 和 tile,大家好像互不相干。可…

作者头像 李华
网站建设 2026/6/11 18:18:37

碧蓝航线Alas自动化脚本终极指南:如何7×24小时智能托管游戏

碧蓝航线Alas自动化脚本终极指南&#xff1a;如何724小时智能托管游戏 【免费下载链接】AzurLaneAutoScript Azur Lane bot (CN/EN/JP/TW) 碧蓝航线脚本 | 无缝委托科研&#xff0c;全自动大世界 项目地址: https://gitcode.com/gh_mirrors/az/AzurLaneAutoScript 厌倦了…

作者头像 李华