点一下关注吧!!!非常感谢!!持续更新!!!
🚀 AI篇持续更新中!(长期更新)
目前2025年06月13日更新到:
AI炼丹日志-28 - Audiblez 将你的电子书epub转换为音频mp3 做有声书,持续打造实用AI工具指南!📐🤖
💻 Java篇正式开启!(300篇)
目前2025年06月11日更新到:
Java-44 深入浅出 Nginx - 底层进程机制 Master Worker 机制原理 常用指令
MyBatis 已完结,Spring 已完结,深入浅出助你打牢基础!
📊 大数据板块已完成多项干货更新(300篇):
包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!
目前2025年06月13日更新到:
大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解
JVM类加载机制
JVM 的类加载机制(Class Loading Mechanism)是 Java 虚拟机中一个非常核心的部分,它决定了类从哪里来、怎么加载、什么时候初始化等一系列行为。下面我会从 整体流程、类加载器模型、双亲委派机制、类生命周期 等几个方面详细描述。
类加载整体流程
Java 类的加载过程可以分为 五个阶段(有些资料是六阶段,把“使用”也列进去):
- 加载(Loading)
- 验证(Verification)
- 准备(Preparation)
- 解析(Resolution)
- 初始化(Initialization)
加载(Loading)
- 把 .class 文件读入内存,生成一个 java.lang.Class 对象。
- 由类加载器完成,可能来自本地磁盘、网络、甚至是动态生成的字节码。
- 指定类的二进制数据的获取方式,不等同于实例化对象。
验证(Verification)
- 确保字节码符合 JVM 规范,保证运行时安全。
- 检查格式是否正确、语义是否符合规定、权限是否合法等。
准备(Preparation)
- 为类的静态变量分配内存,并设置初始默认值(不是显式赋值)。
解析(Resolution)
- 把常量池中的符号引用转换为直接引用。
- 如:类名、字段名、方法签名 -> 实际地址。
初始化(Initialization)
- 正式执行 () 方法(类构造器),也就是执行静态变量的初始化和静态代码块。
- 这是类加载过程的最后一步,之后类才可以使用。
类加载器模型(ClassLoader)
JVM 中的类加载器负责把类加载进 JVM,并维持一个“类名-类实例”的映射表。
- Bootstrap ClassLoader(启动类加载器):JVM 内置,用 C++ 实现,JAVA_HOME/lib 核心类,如 java.lang.*
- Extension ClassLoader(扩展类加载器):加载扩展库,JAVA_HOME/lib/ext
- App ClassLoader(应用类加载器):最常用,默认加载你的代码,classpath 路径下的类
- 自定义 ClassLoader:开发者自定义实现,如热部署、解密等
JVM的类机制中有一个非常重要的角色叫做类加载器(ClassLoader),类加载器有自己的体系,JVM内置了几种类加载器,包括:
● 引导类加载器
● 扩展类加载器
● 系统类加载器
双亲委派机制(Parent Delegation Model)
- 类加载器在加载类之前,会先把请求交给父类加载器,一直向上委托,直到 Bootstrap。
- 只有当父类加载器无法加载,子加载器才会尝试加载。
这样做的原因是:
- 防止核心类被“篡改”或“重复加载”,保证安全性和一致性。
- 如:你不能加载一个伪造的 java.lang.String 类。
比如会进行这样的加载:
AppClassLoader -> ExtClassLoader -> BootstrapClassLoader
类的生命周期(包含类加载阶段)
被主动使用时才会触发类加载(尤其是初始化)
- new 实例
- 访问静态变量
- 调用静态方法
- 反射调用
- 子类初始化
类卸载
- JVM 不再使用某个类,并且加载它的 ClassLoader 被回收,才可能卸载该类(主要出现在 OSGi、Tomcat 这类容器中)。
类加载机制与热部署/插件化的关系
- 热部署(如 Spring Boot DevTools)通常通过自定义 ClassLoader 隔离加载上下文。
- 插件系统(如 IDEA 插件)也依赖于 ClassLoader 的命名空间隔离性。
常见场景
- Tomcat热部署:每次发布使用新的 ClassLoader
- SPI机制:基于 Thread.currentThread().getContextClassLoader()
- 解密加载:重写 findClass 方法
- 沙箱隔离:不同插件使用不同的 ClassLoader
- AOP/热更新:动态修改或替换 Class 对象
图示内容
它们之间形成父子关系,通过Parent属性来定义这种关系,最终可以形成树形结构。
● 引导启动类加载器 BootstrapClassLoader:C++编写,加载Java核心库 java.,比如 rt.jar中的类,构造 ExtClassLoader 和 AppClassLoader
● 扩展类加载器 ExtClassLoader:Java编写,加载扩展库 JAVA_HOME/lib/ext 目录下的 jar 中的类,如 classpath 中的 jre,javax. 或者 java.ext.dir 指定位置中的类
● 系统类加载器 SystemClassLoader/AppClassLoader:默认的加载器,搜索环境变量classpath中指明的路径
另外:用户可以自定义类加载器(Java编写,用户自定义的加载器,可加载指定路径的 class 文件)
当JVM运行过程中,用户自定义了类加载器去加载某些类时,会按照下面的步骤(父类委托机制):
● 用户自己的类加载器,把加载请求传给父加载器,父加载器再传给其父加载器,一直到加载器树的顶层
● 最顶层的类加载器首先针对其特定的位置加载,如果加载不到就转交给子类
● 如果一直到底层的类加载都没有加载到,那么会抛出异常:ClassNotFoundException
因此,按照这个过程可以想到,如果同样在 classpath 指定的目录中和自己工作目录中存放相同的class,会优先加载 classpath 目录中的文件。
双亲委派机制
基本介绍
当某个类加载器需要加载某个.class文件时,它首先把这个任务托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。
机制作用
● 防止重复加载同一个 .class,通过委托机制向上面去问问,加载过了就不加载了,保证数据安全。
● 保证 .class 不能被篡改,通过委托方式,不会去篡改核心 .class,即使篡改也不会加载。即使加载了也不会同一个 .class 对象了。不同的加载器加载同一个.class也不是同一个.class对象。这样保证了class执行安全(如果子类加载器先加载,那么我们写一些与Java.lang包中基础类同名的类,然后再定义一个子类,这样整个应用使用的基础类就变成了我们自定义的类了)
Object类->自定义加载器(可能真正的Object类已经被修改了)
Tomcat的类加载机制
Tomcat 的类加载机制相对于 JVM的类加载机制做了一些改变,没有严格的遵从双亲委派机制,也可以说打破了双亲委派机制。
比如 Tomcat 下,webapps下部署了两个应用:
● app1/lib/a-1.0.jar com.wzk.Abc
● app2/lib/a-2.0jar com.wzk.Abc
不同版本的 Abc 类的内容是不同的,代码是不一样的
● 引导类加载器 和 扩展类加载器 的作用不变
● 系统类加载器正常情况下加载的是 CLASSPATH 下的类,但是 Tomcat 的启动脚本并未使用该变量,而是加载Tomcat启动的类,比如 Bootstrap.jar,通常在 catalina.bat 或者 catalina.sh 中指定,位于 CATALINA_HOME/bin 下。
● Common 通用类的加载器加载 Tomcat 使用以及应用通用的一些类,位于 CATALINA_HOME/lib下,比如 servlet-api.jar
● Catalina ClassLoader 用于加载器内部可见类,这样类应用程序不能访问
● Shared ClassLoader 用于加载应用程序共享类,这些类服务器不会依赖
● Webapp ClassLoader 每个应用程序都会有一个独一无二的 Webapp ClassLoader,他用来加载本应用程序的 /WEB-INF/classes 和 /WEB-INF/lib 下的类。
Tomcat 8.5
Tomcat 8.5 默认改变了严格的双亲委派机制:
● 首先从 Bootstrap ClassLoader 加载指定的类
● 如果未加载到,则从 /WEB-INF/classes 加载
● 如果未加载到,则从 /WEB-INF/*.jar 加载
● 如果未加载到,则依次从 System、Common、Shared 加载(在这最后一步,遵从双亲委派机制)