news 2026/6/10 19:39:07

虚拟机类加载机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
虚拟机类加载机制
Java 虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这个过程被称为虚拟机的类加载机制。

类加载时机

一个类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载、验证、准备、解析、初始化、使用、卸载七个阶段,其中验证、准备、解析三个部分统称为连接:
《Java 虚拟机规范》严格规定了有且只有六种情况必须立即对类进行初始化:
①、遇到newgetstaticputstaticinvokestatic这四条字节码指令,能够生成这四条指令码的典型 Java 代码场景有:
  • 使用new关键字实例化对象时;
  • 读取或设置一个类型的静态字段时(被 final 修饰,已在编译期把结果放入常量池的静态字段除外);
  • 调用一个类的静态方法时。
②、使用java.lang.reflect包的方法对 Class 进行反射调用时,如果类型没有进行过初始化、则需要触发其初始化;
③、当初始化类时,如发现其父类还没有进行过初始化、则需要触发其父类进行初始化;
④、当虚拟机启动时,用户需要指定一个要执行的主类(包含 main() 方法的那个类),虚拟机会先初始化这个主类;
⑤、当使用 JDK 7 新加入的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后解析的结果为REF_getStaticREF_putStaticREF_invokeStaticREF_newInvokeSpecial四种类型的方法句柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化;
⑥、当一个接口中定义了 JDK 8 新加入的默认方法(被 default 关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那么该接口要在其之前被初始化。

类加载过程

1. 加载
在加载阶段,虚拟机需要完成以下三件事:
  • 通过一个类的全限定名来获取定义此类的二进制字节流 ;
  • 将这个字节流所代表的静态存储结构转换为运行时数据结构;
  • 在内存中生成一个代表这个类的java.lang.Class对象,作为这个类的各种数据的访问入口。
《Java 虚拟机规范》并没有限制从何处获取二进制流,因此可以从 JAR 包、WAR 包获取,也可以从 JSP 生成的 Class 文件等处获取。
2. 验证
这一阶段的目的是确保 Class 文件的字节流中包含的信息符合《Java 虚拟机规范》的全部约束要求,从而保证这些信息被当做代码运行后不会危害虚拟机自身的安全。
验证阶段大致会完成下面四项验证:
  • 文件格式验证:验证字节流是否符合 Class 文件格式的规范;
  • 元数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合《Java 语言规范》的要求(如除了java.lang.Object外,所有的类都应该有父类);
  • 字节码验证:通过数据流分析和控制流分析,确定程序语义是合法的,符合逻辑的(如允许把子类对象赋值给父类数据类型,但不能把父类对象赋值给子类数据类型);
  • 符号引用验证:验证类是否缺少或者被禁止访问它依赖的某些外部类、方法、字段等资源。如果无法验证通过,则会抛出一个java.lang.IncompatibleClassChangeError的子类异常,如java.lang.NoSuchFieldErrorjava.lang.NoSuchMethodError等。
3. 准备
准备阶段是正式为类中定义的变量(即静态变量,被 static 修饰的变量)分配内存并设置类变量初始值的阶段。
4. 解析
解析是 Java 虚拟机将常量池内的符号引用替换为直接引用的过程:
  • 符号引用:符号引用用一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。
  • 直接引用:直接引用是指可以直接指向目标的指针、相对偏移量或者一个能间接定位到目标的句柄。
整个解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符这 7 类符号引用进行解析。
5. 初始化
初始化阶段就是执行类构造器的()方法的过程,该方法具有以下特点:
  • ()方法由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生,编译器收集顺序由语句在源文件中出现的顺序决定。
  • ()方法与类的构造方法(即在虚拟机视角中的实例构造器()方法)不同,它不需要显示的调用父类的构造器,Java 虚拟机会保证在子类的()方法执行前,父类的()方法已经执行完毕。
  • 由于父类的()方法先执行,也就意味着父类中定义的静态语句块要优先于子类变量的赋值操作。
  • ()方法对于类或者接口不是必须的,如果一个类中没有静态语句块,也没有对变量进行赋值操作,那么编译器可以不为这个类生成()方法。
  • 接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生成()方法。
  • Java 虚拟机必须保证一个类的()方法在多线程环境中被正确的加锁同步,如果多个线程同时去初始化一个类,那么只会有其中一个线程去执行这个类的()方法,其他线程都需要阻塞等待。

类加载器

能够通过一个类的全限定名来获取描述该类的二进制字节流的工具称为类加载器。
每一个类加载器都拥有一个独立的类名空间,因此对于任意一个类,都必须由加载它的类加载器和这个类本身来共同确立其在 Java 虚拟机中的唯一性。
这意味着要想比较两个类是否相等,必须在同一类加载器加载的前提下;如果两个类的类加载器不同,则它们一定不相等。

双亲委派模型

从 Java 虚拟机角度而言,类加载器可以分为以下两类:
  • 启动类加载器:启动类加载器(Bootstrap ClassLoader)由 C++ 语言实现(以 HotSpot 为例),它是虚拟机自身的一部分;
  • 其他所有类的类加载器:由 Java 语言实现,独立存在于虚拟机外部,并且全部继承自java.lang.ClassLoader
从开发人员角度而言,类加载器可以分为以下三类:
  • 启动类加载器 (Boostrap Class Loader):负责把存放在\lib目录中,或被-Xbootclasspath参数所指定的路径中存放的能被 Java 虚拟机识别的类库加载到虚拟机的内存中;
  • 扩展类加载器 (Extension Class Loader):负责加载\lib\ext目录中,或被java.ext.dirs系统变量所指定的路径中的所有类库。
  • 应用程序类加载器 (Application Class Loader):负责加载用户类路径(ClassPath)上的所有的类库。
JDK 9 之前的 Java 应用都是由这三种类加载器相互配合来完成加载:
上图所示的各种类加载器之间的层次关系被称为类加载器的 “双亲委派模型”,“双亲委派模型” 要求除了顶层的启动类加载器外,其余的类加载器都应该有自己的父类加载器,需要注意的是这里的加载器之间的父子关系一般不是以继承关系来实现的,而是使用组合关系来复用父类加载器的代码。
双亲委派模型的工作过程如下:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。
基于双亲委派模型可以保证程序中的类在各种类加载器环境中都是同一个类,否则就有可能出现一个程序中存在两个不同的java.lang.Object的情况。

模块化下的类加载器

JDK 9 之后为了适应模块化的发展,类加载器做了如下变化:
  • 仍维持三层类加载器和双亲委派的架构,但扩展类加载器被平台类加载器所取代;
  • 当平台及应用程序类加载器收到类加载请求时,要首先判断该类是否能够归属到某一个系统模块中,如果可以找到这样的归属关系,就要优先委派给负责那个模块的加载器完成加载;
  • 启动类加载器、平台类加载器、应用程序类加载器全部继承自java.internal.loader.BuiltinClassLoader,BuiltinClassLoader 中实现了新的模块化架构下类如何从模块中加载的逻辑,以及模块中资源可访问性的处理。

面试题

简述JVM类加载过程
1)加载:
  • 通过全类名获取类的二进制字节流。
  • 将类的静态存储结构转化为方法区的运行时数据结构。
  • 在内存中生成类的Class对象,作为方法区数据的入口。
2)验证:对文件格式,元数据,字节码,符号引用等验证正确性。
3)准备:在方法区内为类变量分配内存并设置为0值。
4)解析:将符号引用转化为直接引用。
5)初始化:执行类构造器clinit方法,真正初始化。
简述JVM中的类加载器
  • BootstrapClassLoader启动类加载器:加载/lib下的jar包和类。 由C++编写。
  • ExtensionClassLoader扩展类加载器: /lib/ext目录下的jar包和类。由Java编写。
  • AppClassLoader应用类加载器,加载当前classPath下的jar包和类。由Java编写。
简述双亲委派机制
一个类加载器收到类加载请求之后,首先判断当前类是否被加载过。已经被加载的类会直接返回,如果没有被加载,首先将类加载请求转发给父类加载器,一直转发到启动类加载器,只有当父类加载器无法完成时才尝试自己加载。
加载类顺序:BootstrapClassLoader->ExtensionClassLoader->AppClassLoader->CustomClassLoader 检查类是否加载顺序: CustomClassLoader->AppClassLoader->ExtensionClassLoader->BootstrapClassLoader
双亲委派机制的优点
  • 避免类的重复加载。相同的类被不同的类加载器加载会产生不同的类,双亲委派保证了Java程序的稳定运行。
  • 保证核心API不被修改。
  • 如何破坏双亲委派机制
  • 重载loadClass()方法,即自定义类加载器。
如何构建自定义类加载器
新建自定义类继承自java.lang.ClassLoader,重写findClass、loadClass、defineClass方法

以上就是这个博客的全部内容了!!!

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

AI智能实体侦测服务安全配置:API访问权限控制设置教程

AI智能实体侦测服务安全配置:API访问权限控制设置教程 1. 引言 1.1 业务场景描述 随着AI技术在信息抽取领域的广泛应用,命名实体识别(NER)已成为文本分析的核心能力之一。特别是在新闻处理、舆情监控、知识图谱构建等场景中&am…

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

RaNER模型应用案例:电商商品描述实体抽取

RaNER模型应用案例:电商商品描述实体抽取 1. 引言:AI 智能实体侦测服务的业务价值 在电商场景中,海量的商品描述文本蕴含着丰富的关键信息——品牌名、型号、产地、适用人群等。然而这些数据通常以非结构化形式存在,难以直接用于…

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

Qwen2.5-7B持续集成:自动部署最新社区模型

Qwen2.5-7B持续集成:自动部署最新社区模型 引言:为什么需要自动部署最新模型? 在开源社区中,像Qwen2.5-7B这样的先进语言模型几乎每天都在迭代更新。官方团队会不断优化模型性能、修复已知问题并添加新功能。但对于依赖这个模型…

作者头像 李华
网站建设 2026/6/10 11:10:05

Qwen2.5-7B极速体验:从零到运行只要10分钟,不烧钱

Qwen2.5-7B极速体验:从零到运行只要10分钟,不烧钱 1. 为什么选择Qwen2.5-7B? 作为一名技术博主,我经常需要测试各种AI模型。最近在准备Qwen2.5评测视频时,发现本地环境已经被之前的项目搞得一团糟。重装系统太耗时&a…

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

AI智能实体侦测服务API开发指南:从入门到精通

AI智能实体侦测服务API开发指南:从入门到精通 1. 引言 1.1 业务背景与技术需求 在当今信息爆炸的时代,非结构化文本数据(如新闻、社交媒体内容、文档资料)呈指数级增长。如何从中高效提取关键信息,成为自然语言处理…

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

基于AI智能实体侦测的搜索优化:企业知识库构建案例

基于AI智能实体侦测的搜索优化:企业知识库构建案例 1. 引言:从非结构化文本到可检索知识 在现代企业信息化建设中,知识库已成为支撑决策、客户服务和内部协作的核心资产。然而,大量业务数据以非结构化文本形式存在——如会议纪要…

作者头像 李华