彻底理解JVM类加载机制
文章目录
- 一、类加载器和双亲委派机制
- 1.1、类加载器
- 1.2、双亲委派机制
- 1.3、自定义类加载器
- 1.4、打破双亲委派机制
- 二、类的加载
图片来源:图灵学院
由上图可知,创建对象,执行其中的方法,在java层面,最重要的有获取类加载器以及加载类两部分
一、类加载器和双亲委派机制
1.1、类加载器
在JVM中,类加载器分为:
- 启动类加载器:最核心的类加载器,用于加载JRE的lib目录下的
核心类库。 - 扩展类加载器:负责加载位于JRE的lib目录下的ext扩展目录中的JAR类包。
- 应用程序类加载器:负责加载ClassPath路径下的类包。
- 自定义类加载器:用于加载用户自定义路径的类包。
sun.misc包下的Launcher类,会在构造方法中对类加载器进行初始化:
构造方法中的关键部分:
sun.misc.Launcher.AppClassLoader#getAppClassLoader(获取应用程序类加载器方法)的最底层,将参数中传入的扩展类加载器,赋值给成员变量。
ExtClassLoader扩展类加载器继承自ClassLoader

1.2、双亲委派机制
双亲委派机制的核心是,向上委派,向下加载:
即当加载某个类时,会从最底层的类加载器开始,逐个向上查找目标类,如果加载过,就直接返回。如果查找到最上层依旧没有,则从最上层自身负责的类加载路径中查找并加载。
- 自己写的一个User类,第一次运行,应用程序类加载器没有找到,向上查找扩展类、启动类加载器,依然没有找到。就从最顶层的启动类加载器开始,查看该类是否在自己的加载范围内。很显然自定义的类,不在启动类加载器的范围内,向下委派到扩展类加载器,发现依旧不在自身的范围内,就再次委派给应用程序类加载器,这次发现在自己的加载范围内,就会加载。
- java.lang包下的String类,第一次运行,应用程序类加载器没有找到,向上查找扩展类、启动类加载器,依然没有找到。就从最顶层的启动类加载器开始,查看该类是否在自己的加载范围内。java.lang包下的String类属于
核心类库,启动类加载器发现它在自己应该加载的范围内,就会进行加载。
双亲委派机制在源码中的体现在于java.lang.ClassLoader#loadClass(java.lang.String, boolean):
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {//首先查看当前的类加载器是否加载过目标(底层是c/c++实现的方法)Class<?> c = findLoadedClass(name);//当前的类加载器没有加载过if (c == null) {long t0 = System.nanoTime();try {//扩展类加载器进行查找//这里的parent属性是当前类加载器的父加载器if (parent != null) {c = parent.loadClass(name, false);}//启动类加载器进行查找 else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}//都没有加载过if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();//查看当前的类路径,判断是否应该是自己加载。c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}
第一次加载某个自定义类时的流程:
应用程序类加载器发现自身没有加载过,去执行扩展类加载器的loadClass方法:
扩展类加载器发现自身没有加载过,去找启动类加载器(底层由c/c++执行)
启动类加载器返回的c为空,在扩展类加载器进入if判断,尝试自己加载,但是自定义的类不在自己的加载范围内,将null值的c返回到应用程序类加载器:
应用程序类加载器得到扩展类加载器的返回为空,就尝试自己加载并且返回:
1.3、自定义类加载器
自定义类加载器,需要继承ClassLoader,并且重写其中的findClass,loadClass方法:
public class MyClassLoaderTest1 {static class MyClassLoader extends ClassLoader {private String classPath;public MyClassLoader(String classPath) {this.classPath = classPath;}private byte[] loadByte(String name) throws Exception {name = name.replaceAll("\\.", "/");FileInputStream fis = new FileInputStream(classPath + "/" + name+ ".class");int len = fis.available();byte[] data = new byte[len];fis.read(data);fis.close();return data;}protected Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] data = loadByte(name);//defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。return defineClass(name, data, 0, data.length);} catch (Exception e) {e.printStackTrace();throw new ClassNotFoundException();}}}public static void main(String args[]) throws Exception {//初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoaderMyClassLoader classLoader = new MyClassLoader("D:/test");//D盘创建 test/com/tuling/jvm 几级目录,将User类的复制类User1.class丢入该目录Class clazz = classLoader.loadClass("com.itbaima.jvm.User");Object obj = clazz.newInstance();Method method = clazz.getDeclaredMethod("sout", null);method.invoke(obj, null);System.out.println(clazz.getClassLoader().getClass().getName());}
}
上面的自定义类加载器,目的是为了加载D盘test文件夹下的com.itbaima.jvm的User.class文件,假设目前的classpath下和D盘指定目录下都有该文件?

那么根据双亲委派机制,User类应该由应用程序类加载器,而非自定义类加载器加载。原因很简单,当User类被启动类加载器向下加载时,在应用程序类加载器这一层,发现属于自己的classpath范围内有这个类,就会进行加载。
把classpath下的User类删除,则由自定义类加载器进行加载:

1.4、打破双亲委派机制
打破双亲委派机制的关键在于自定义实现loadClass方法。在1.3案例的基础上,继续重写loadClass方法:
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();//让父类去加载Objectif (!name.startsWith("com.itbaima.jvm")) {c = this.getParent().loadClass(name);}else {c = findClass(name);}// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}if (resolve) {resolveClass(c);}return c;}
在重写的loadClass方法中,去除了向上委派的逻辑,而是由自身进行加载,为什么还要加上
if (!name.startsWith("com.itbaima.jvm")) {c = this.getParent().loadClass(name);}
的判断?原因在于,用当前的类加载器去加载User类没有问题,但是User类继承了java.lang包下的Object类,如果把判断条件去除:
如果把Object.class文件复制一份到指定的目录呢?
答案也是否定的,因为在类加载的过程中JVM还有自己的安全监测机制,是不会允许核心的类被自定义加载的。
二、类的加载
类的加载,通过loadClass方法,会经历验证、准备、解析、初始化四个阶段:
- 验证阶段:主要是校验class文件是否符合规范,以及对正确性进行校验,最经典的是cafe babe模数校验。
- 准备阶段:给类的静态属性分配内存,并且赋初值(基本数据类型为默认值,引用类型为null)。
- 解析阶段:将
符号引用转变为直接引用,符号引用简单可以理解为类中的方法名,变量名等。而直接引用,是符号引用真正指向的内存地址。 - 初始化:在这一步才是给类的静态属性赋值,并且执行静态代码块。
静态代码块的执行先于构造方法:

相关文章:
彻底理解JVM类加载机制
文章目录 一、类加载器和双亲委派机制1.1、类加载器1.2、双亲委派机制1.3、自定义类加载器1.4、打破双亲委派机制 二、类的加载 图片来源:图灵学院 由上图可知,创建对象,执行其中的方法,在java层面,最重要的有获取…...
【计算机体系结构、微架构性能分析】core 与 uncore 分别是哪一些部分?区分 core 和 uncore
在计算机体系结构中,Core 和 Uncore 是描述处理器内部架构的两个重要概念,尤其在多核处理器中更为常见。 1. Core(核心) Core 指的是处理器中的计算核心,是执行指令和处理数据的基本单元。每个核心都包含独立的执行单…...
鸿蒙打包发布
HarmonyOS应用/元服务发布(打包发布) https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/ide-publish-app-V13?catalogVersionV13 密钥:包含非对称加密中使用的公钥和私钥,存储在密钥库文件中,格式…...
vue2:实现上下两栏布局,可拖拽改变高度
要拖拽改变两栏高度,那么总高度要确定,在拖拽的过程中,实时根据光标位置计算两栏高度,所以: 1、最外层有一个box, 高度是屏幕高度screenHeight; 2、该值在页面挂载时获取初始值(window.innerHeight-100),这里减少100,因为窗口上面有工具栏; 3、监听窗口resize事件…...
MongoDB 学习指南:深入探索非关系型数据库
MongoDB学习资料 MongoDB学习资料 MongoDB学习资料 在当今数字化时代,数据量呈爆炸式增长,数据结构也变得愈发复杂多样。传统的关系型数据库在处理一些大规模、高并发以及非结构化数据时,逐渐显露出局限性。而 MongoDB 作为一款领先的非关系…...
天机学堂3-ES+Caffeine
文章目录 day05-问答系统表 用户端分页查询问题目标效果代码实现 3.6.管理端分页查询问题ES相关 管理端互动问题分页实现三级分类3.6.5.2.多级缓存3.6.5.3.CaffeineTODO:使用Caffeine作为本地缓存,另外使用redis或者memcache作为分布式缓存,构…...
FPGA车牌识别
基于FPGA的车牌识别主要包含以下几个步骤:图像采集、颜色空间转换、边缘检测、形态学处理(腐蚀和膨胀)、特征值提取、模板匹配、结果显示。先用matlab对原理进行仿真,后用vivado和modelsim进行设计和仿真。 一、1.图像采集采用ov…...
Pandas库的常用内容归纳
Pandas 是一个强大的 Python 数据分析库,提供了大量用于数据处理和分析的功能。以下是一些 Pandas 库中常用的功能: 数据创建和操作 Series 和 DataFrame:创建一维的 Series 和二维的 DataFrame 对象。数据导入:从 CSV、Excel、…...
R语言的并发编程
R语言的并发编程 引言 在现代计算中,如何有效地利用计算资源进行数据处理和分析已成为一个重要的研究方向。尤其在大数据时代,数据量的急剧增加让单线程处理方式显得力不从心。为了解决这一问题,各种编程语言都开展了并发编程的研究和应用。…...
STM32 FreeRTOS中断管理
目录 FreeRTOS的中断管理 1、STM32中断优先级管理 2、FreeRTOS任务优先级管理 3、寄存器和内存映射寄存器 4、BASEPRI寄存器 5、FreeRTOS与STM32中断管理结合使用 vPortRaiseBASEPRI vPortSetBASEPRI 6、FromISR后缀 7、在中断服务函数中调用FreeRTOS的API函数需注意 F…...
数据结构-栈和队列
文章目录 一、栈1.概念与结构2.数组栈的实现2.1 栈的结构定义2.2 栈的初始化2.3 栈的销毁2.4 栈的判空2.5 栈的入栈2.6 栈的出栈2.7 查看栈顶元素2.8 栈的大小 3.两种栈的图示结构 二、队列1.概念与结构2.链式队列的实现2.1 队列的结构定义2.2 队列的初始化2.3 队列的销毁2.4 队…...
RabbitMQ---TTL与死信
(一)TTL 1.TTL概念 TTL又叫过期时间 RabbitMQ可以对队列和消息设置TTL,当消息到达过期时间还没有被消费时就会自动删除 注:这里我们说的对队列设置TTL,是对队列上的消息设置TTL并不是对队列本身,不是说队列过期时间…...
第4章 Kafka核心API——Kafka客户端操作
Kafka客户端操作 一. 客户端操作1. AdminClient API 一. 客户端操作 1. AdminClient API...
Python爬虫学习前传 —— Python从安装到学会一站式服务
早上好啊,大佬们。我们的python基础内容的这一篇终于写好了,啪唧啪唧啪唧…… 说实话,这一篇确实写了很久,一方面是在忙其他几个专栏的内容,再加上生活学业上的事儿,确实精力有限,另一方面&…...
Lora理解QLoRA
Parameter-Efficient Fine-Tuning (PEFT) :节约开销的做法,fine-tune少量参数,而不是整个模型; Low-Rank Adaptation (LoRA) :是PEFT的一种;冻结原参数矩阵,只更新2个小参数矩阵。 原文经过对比…...
Linux测试处理fps为30、1920*1080、一分钟的视频性能
前置条件 模拟fps为30、1920*1080、一分钟的视频 项目CMakeLists.txt cmake_minimum_required(VERSION 3.30) project(testOpenGl)set(CMAKE_CXX_STANDARD 11)add_executable(testOpenGl main.cpptestOpenCl.cpptestOpenCl.hTestCpp.cppTestCpp.hTestCppThread.cppTestCppTh…...
Flink (六):DataStream API (三) 窗口
1. 窗口 窗口(Window)是处理无界流的关键所在。窗口可以将数据流装入大小有限的“桶”中,再对每个“桶”加以处理。 下面展示了 Flink 窗口在 keyed streams 和 non-keyed streams 上使用的基本结构。 我们可以看到,这两者唯一的…...
MYSQL学习笔记(二):基本的SELECT语句使用(基本、条件、聚合函数查询)
前言: 学习和使用数据库可以说是程序员必须具备能力,这里将更新关于MYSQL的使用讲解,大概应该会更新30篇,涵盖入门、进阶、高级(一些原理分析);这一篇是讲解SELECT语句使用,包括基本、条件、聚合函数查询,…...
PCL 点到面的ICP算法实现点云配准(C++详细过程版)
ICP算法 一、算法原理1、算法概述2、实现流程3、参考文献二、代码实现三、结果展示四、相关链接一、算法原理 1、算法概述 实现的算法与 PCL 点到面的ICP精配准(线性最小二乘优化)一文相同,使用C++代码复现线性优化的求解过程,求解过程如下所示,由于原版英文文献的计算过…...
MarsCode青训营打卡Day1(2025年1月14日)|稀土掘金-16.最大矩形面积问题
资源引用: 最大矩形面积问题 - MarsCode 打卡小记录: 今天是开营第一天,和小伙伴们组成了8人的团队,在接下来的数十天里相互监督,打卡刷题! 稀土掘金-16.最大矩形面积问题(16.最大矩形面积问题…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
