JVM 的类加载机制原理

JVM 的类加载机制是指 JVM 将 .class 文件(包含 Java 字节码)加载到内存,并对其进行校验、解析、初始化,最终转换为 JVM 可以直接使用的 Java 类型的过程。

类加载过程 (5 个阶段):

  1. 加载 (Loading):

    • 查找并加载类的二进制数据:
      • 通过类的全限定名(Fully Qualified Name)查找 .class 文件。
      • 类加载器(ClassLoader)负责查找和加载 .class 文件。类加载器有多种,包括启动类加载器、扩展类加载器、应用程序类加载器,以及自定义类加载器。
      • 类加载器遵循双亲委派模型(Parent Delegation Model)。
      • 将找到的 .class 文件中的二进制数据读取到内存中。
    • 创建类的 Class 对象:
      • 在方法区中创建一个 java.lang.Class 对象,作为该类的访问入口。
      • 这个 Class 对象封装了类的结构信息(类名、父类、接口、字段、方法、注解等)。
  2. 链接 (Linking):

    • 验证 (Verification):
      • 目的: 确保加载的类文件符合 JVM 规范,并且不会危害 JVM 的安全。
      • 检查内容:
        • 文件格式验证: 检查 .class 文件是否符合 Java 类文件规范(魔数、版本号等)。
        • 元数据验证: 检查类的元数据是否正确(例如,是否有父类、是否实现了接口、字段和方法的描述符是否合法等)。
        • 字节码验证: 检查字节码指令是否合法、安全(例如,类型检查、控制流检查、操作数栈检查等)。这是最复杂的一个阶段。
        • 符号引用验证: 检查符号引用是否能够找到对应的类、字段或方法。
    • 准备 (Preparation):
      • 目的: 为类的静态变量(static variables)分配内存,并设置默认初始值(零值)。
      • 注意:
        • 这里分配内存的仅包括类变量(static 修饰的变量),不包括实例变量(实例变量会在对象实例化时随着对象一起分配在 Java 堆中)。
        • 这里设置的初始值是“零值”(例如,int 类型的零值是 0,boolean 类型的零值是 false,引用类型的零值是 null),而不是代码中显式赋予的值(显式赋值是在初始化阶段进行的)。
        • 对于 static final 修饰的基本类型或字符串常量, 会直接在准备阶段赋值.
    • 解析 (Resolution):
      • 目的: 将类、接口、字段和方法的符号引用(symbolic references)解析为直接引用(direct references)。
      • 符号引用: 以符号(例如,类的全限定名、方法的名称和描述符)来表示目标。
      • 直接引用: 可以直接定位到目标的指针、偏移量或句柄。
      • 解析的时机:
        • Java 虚拟机规范并没有强制规定解析阶段的具体时间,可以延迟到运行时(用到的时候再解析)。
        • HotSpot VM 中,解析通常是延迟进行的。
      • 解析的内容:
        • 类或接口的解析
        • 字段解析
        • 类方法解析
        • 接口方法解析
  3. 初始化 (Initialization):

    • 目的: 执行类的初始化代码,为静态变量赋初始值(代码中显式指定的值),并执行静态代码块。
    • 触发时机:
      • 遇到 newgetstaticputstaticinvokestatic 这四条字节码指令时(创建对象、访问静态字段、调用静态方法)。
      • 使用 java.lang.reflect 包的方法对类进行反射调用时。
      • 初始化一个类的子类时(会先初始化其父类)。
      • 虚拟机启动时,用户需要指定一个要执行的主类(包含 main 方法的类),虚拟机会先初始化这个主类。
      • 使用JDK 1.7 的动态语言支持时(invokedynamic指令).
      • 接口的初始化: 当一个接口中定义了 default 方法时, 如果这个接口的实现类发生了初始化, 则该接口要在实现类之前被初始化.
    • 执行内容:
      • 执行 <clinit>() 方法(类构造器)。
        • <clinit>() 方法是由编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并产生的。
        • <clinit>() 方法的执行顺序是按照语句在源文件中出现的顺序决定的。
        • <clinit>() 方法与类的构造函数(<init>() 方法)不同,它不需要显式地调用父类构造器,虚拟机会保证在子类的 <clinit>() 方法执行之前,父类的 <clinit>() 方法已经执行完毕。
        • 如果一个类中没有静态语句块,也没有对类变量的赋值操作,那么编译器可以不为这个类生成 <clinit>() 方法。
        • 接口中不能使用静态代码块, 但接口也需要通过<clinit>()方法为接口中定义的静态成员变量显示初始化.
        • 接口的<clinit>()方法不需要先执行父接口的<clinit>()方法.
        • 虚拟机会保证一个类的 <clinit>() 方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的 <clinit>() 方法,其他线程都需要阻塞等待,直到活动线程执行 <clinit>() 方法完毕。

类加载器 (ClassLoader):

  • 类型:

    • 启动类加载器 (Bootstrap Class Loader): C++ 实现, 负责加载 Java 核心类库(<JAVA_HOME>/jre/lib)。
    • 扩展类加载器 (Extension Class Loader): Java 实现(sun.misc.Launcher$ExtClassLoader), 负责加载 Java 扩展类库(<JAVA_HOME>/jre/lib/extjava.ext.dirs 指定的目录)。
    • 应用程序类加载器 (Application Class Loader): Java 实现(sun.misc.Launcher$AppClassLoader), 负责加载应用程序的类(classpath)。
    • 自定义类加载器: 继承 java.lang.ClassLoader,实现自定义的类加载逻辑。
  • 双亲委派模型 (Parent Delegation Model):

    • 除了启动类加载器,每个类加载器都有一个父类加载器。
    • 当一个类加载器需要加载类时,它首先会委托给它的父类加载器去加载。
    • 只有当父类加载器无法加载该类时(在其搜索范围内找不到该类),才由子类加载器尝试加载。
    • 优点:
      • 避免类的重复加载。
      • 保证 Java 核心类库的安全性(防止用户自定义的类替换核心类)。

类加载机制的特点:

  • 动态加载: 类加载是在程序运行时进行的,而不是在编译时。这使得 Java 具有动态性和灵活性。
  • 按需加载: 类加载器只在需要时才加载类,而不是一次性加载所有类。这可以节省内存空间。
  • 双亲委派: 双亲委派模型保证了类加载的顺序和安全性。
  • 缓存机制: 类加载器会缓存已加载的类,避免重复加载。

代码示例 (自定义类加载器):

import java.io.*;public class MyClassLoader extends ClassLoader {private String classpath;public MyClassLoader(String classpath) {this.classpath = classpath;}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] classData = loadClassData(name);if (classData == null) {throw new ClassNotFoundException();} else {return defineClass(name, classData, 0, classData.length);}} catch (IOException e) {throw new ClassNotFoundException();}}private byte[] loadClassData(String className) throws IOException {String fileName = classpath + File.separatorChar+ className.replace('.', File.separatorChar) + ".class";try (InputStream ins = new FileInputStream(fileName);ByteArrayOutputStream baos = new ByteArrayOutputStream()) {int bufferSize = 1024;byte[] buffer = new byte[bufferSize];int length = 0;while ((length = ins.read(buffer)) != -1) {baos.write(buffer, 0, length);}return baos.toByteArray();}}public static void main(String[] args) throws Exception{//测试自定义类加载器MyClassLoader myClassLoader = new MyClassLoader("./myclasses"); // 指定类路径Class<?> clazz = myClassLoader.loadClass("com.example.MyClass"); // 加载类Object obj = clazz.newInstance();  //实例化System.out.println(obj.getClass().getClassLoader());//打印类加载器}
}

总结:

JVM 的类加载机制负责将 .class 文件加载到内存中,并将其转换为 JVM 可以使用的 Java 类型。类加载过程包括加载、链接(验证、准备、解析)和初始化几个阶段。类加载器遵循双亲委派模型,保证了类加载的顺序和安全性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/36204.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【css酷炫效果】纯CSS实现悬浮弹性按钮

【css酷炫效果】纯CSS实现悬浮弹性按钮 缘创作背景html结构css样式完整代码效果图 想直接拿走的老板&#xff0c;链接放在这里&#xff1a;https://download.csdn.net/download/u011561335/90492020 缘 创作随缘&#xff0c;不定时更新。 创作背景 刚看到csdn出活动了&…

【模块化编程】制作、可视化不平衡数据(长尾数据)(以Cifar-10为例)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;《PyTorch科研加速指南&#xff1a;即插即用式模块开发》-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎…

Linux操作系统7- 线程同步与互斥1(POSIX互斥锁的使用详解)

上篇文章&#xff1a;Linux操作系统6- 线程4&#xff08;POSIX线程的简单封装&#xff09;-CSDN博客 本篇代码仓库:myLerningCode/l30 橘子真甜/Linux操作系统与网络编程学习 - 码云 - 开源中国 (gitee.com) 目录 一. 线程不互斥造成的结果 二. pthread_mutex_t 互斥…

深入 Linux 声卡驱动开发:核心问题与实战解析

1. 字符设备驱动如何为声卡提供操作接口&#xff1f; 问题背景 在 Linux 系统中&#xff0c;声卡被抽象为字符设备。如何通过代码让应用程序能够访问声卡的录音和播放功能&#xff1f; 核心答案 1.1 字符设备驱动的核心结构 Linux 字符设备驱动通过 file_operations 结构体定…

洛谷 [语言月赛 202503] 题解(C++)

本文为洛谷3月的语言月赛题目全部题解&#xff0c;难度为入门到普及-&#xff0c; 觉的有帮助或者写的不错的可以点个赞 题目链接为 题目列表 - 洛谷 | 计算机科学教育新生态 目录 题目A:长方形 解题思路: 代码(C): 题目B:水流 题目大意: 解题思路: 代码(C): 题目C:格…

算法每日一练 (15)

&#x1f4a2;欢迎来到张胤尘的技术站 &#x1f4a5;技术如江河&#xff0c;汇聚众志成。代码似星辰&#xff0c;照亮行征程。开源精神长&#xff0c;传承永不忘。携手共前行&#xff0c;未来更辉煌&#x1f4a5; 文章目录 算法每日一练 (15)第 N 个泰波那契数题目描述解题思路…

实验11 机器学习-贝叶斯分类器

实验11 机器学习-贝叶斯分类器 一、实验目的 &#xff08;1&#xff09;理解并熟悉贝叶斯分类器的思想和原理&#xff1b; &#xff08;2&#xff09;熟悉贝叶斯分类器的数学推导过程&#xff1b; &#xff08;3&#xff09;能运用贝叶斯分类器解决实际问题并体会算法的效果&a…

Matrix-breakout-2-morpheus靶机实战攻略

1.安装并开启靶机 2.获取靶机IP 3.浏览器访问靶机 4.扫描敏感目录文件和端口 gobuster dir -u http://192.168.52.135 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,txt,html 5.访问文件和端口 发现在graffiti.php输入框输入内容后页面会返回内容…

【知识】Graph Sparsification、Graph Coarsening、Graph Condensation的详细介绍和对比

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 1. 理论基础&#xff08;Definitions & Theoretical Background&#xff09; 2. 算法方法&#xff08;Techniques & Algorithms&#x…

Java单元测试、Junit、断言、单元测试常见注解、单元测试Maven依赖范围、Maven常见问题解决方法

一. 测试 1. 测试&#xff1a;是一种用来促进鉴定软件的正确性、完整性、安全性和质量的过程 2. 阶段划分&#xff1a;单元测试、集成测试、系统测试、验收测试。 ① 单元测试&#xff1a;对软件的基本组成单位进行测试&#xff0c;最小测试单位&#xff1b;目的检验软件基本组…

【Notepad】Notepad优化笔记AutoHotkey语法高亮\设置替换默认的notepad程序\设置主题\增加返回上一个编辑地方插件

Npp使用优化笔记 AHK或自定义语法高亮设置替换系统默认的notepad设置主题返回上一次编辑的地方插件使用 AHK或自定义语法高亮 具体参考该论坛 https://www.autohotkey.com/boards/viewtopic.php?t50 设置替换默认的notepad程序 参考文章&#xff1a; https://www.winhelpo…

Mac:Maven 下载+安装+环境配置(详细讲解)

&#x1f4cc; 下载 Maven 下载地址&#xff1a;https://maven.apache.org/download.cgi &#x1f4cc; 无需安装 Apache官网下载 Maven 压缩包&#xff0c;无需安装&#xff0c;下载解压后放到自己指定目录下即可。 按我自己的习惯&#xff0c;我会在用户 jane 目录下新建…

[K!nd4SUS 2025] Crypto

最后一个把周末的补完。这个今天问了小鸡块神终于把一个补上&#xff0c;完成5/6&#xff0c;最后一个网站也上不去不弄了。 Matrices Matrices Matrices 这个是不是叫LWE呀&#xff0c;名词忘了&#xff0c;但意思还是知道。 b a*s e 这里的e是高斯分成&#xff0c;用1000…

学习threejs,构建THREE.ParametricGeometry参数化函数生成几何体

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.ParametricGeometry1…

Canal 解析与 Spring Boot 整合实战

一、Canal 简介 1.1 Canal 是什么&#xff1f; Canal 是阿里巴巴开源的一款基于 MySQL 数据库增量日志解析&#xff08;Binlog&#xff09;中间件&#xff0c;它模拟 MySQL 的从机&#xff08;Slave&#xff09;行为&#xff0c;监听 MySQL 主机的二进制日志&#xff08;Binl…

【海螺AI视频】蓝耘智算 | AI视频新浪潮:蓝耘MaaS与海螺AI视频创作体验

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈人工智能与大模型应用 ⌋ ⌋ ⌋ 人工智能&#xff08;AI&#xff09;通过算法模拟人类智能&#xff0c;利用机器学习、深度学习等技术驱动医疗、金融等领域的智能化。大模型是千亿参数的深度神经网络&#xff08;如ChatGPT&…

Prometheus使用

介绍&#xff1a;Prometheus 是一个开源的 监控与告警系统&#xff0c;主要用于采集和存储时间序列数据&#xff08;Time Series Data&#xff09; Prometheus的自定义查询语言PromQL Metric类型 为了能够帮助用户理解和区分这些不同监控指标之间的差异&#xff0c;Prometheu…

Linux 文件操作-标准IO函数3- fread读取、fwrite写入、 fprintf向文件写入格式化数据、fscanf逐行读取格式化数据的验证

目录 1. fread 从文件中读取数据 1.1 读取次数 每次读取字节数 < 原内容字节数 1.2 读取次数 每次读取字节数 > 原内容字节数 2.fwrite 向文件中写入数据 2.1写入字符串验证 2.2写入结构体验证 3. fprintf 将数据写入到指定文件 4. fscanf 从文件中逐行读取内容…

再学:abi编码 地址类型与底层调用

目录 1.内置全局变量及函数 2.abi 3.地址类型 4.transfer 1.内置全局变量及函数 2.abi data就是abi编码 abi描述&#xff1a;以json格式表明有什么方法 3.地址类型 4.transfer x.transfer:合约转给x call 和 delegatecall 是 Solidity 中用于底层合约调用的函数&#xff0…

解决前端文字超高度有滚动条的情况下padding失效(el-scrollbar)使用

<div class"detailsBlocksContent"><div>测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试…