目录
第一章:类加载概述
1.1 什么是类加载
类加载是 JVM 将字节码文件(.class)加载到内存中,并转换为可以被 JVM 执行的 Class 对象的过程。
1.2 类加载的作用
- 字节码验证:确保字节码文件符合 JVM 规范
- 内存分配:为类分配内存空间
- 符号引用解析:将符号引用转换为直接引用
- 初始化:执行类的初始化代码
1.3 类加载的时机
- 创建类的实例
- 访问类的静态变量
- 调用类的静态方法
- 使用反射访问类
- 初始化子类时,父类会被加载
- 虚拟机启动时,包含 main 方法的类会被加载
第二章:类加载过程
2.1 类加载的完整生命周期
类加载过程分为七个阶段:加载 、验证 、准备 、解析 、初始化 、使用 、卸载
java
// 类加载过程示例
public class ClassLoadingProcess {
static {
System.out.println("静态代码块执行 - 初始化阶段");
}
private static int staticVar = 10; // 准备阶段分配内存,初始化阶段赋值
public static void main(String[] args) {
System.out.println("main 方法执行");
System.out.println("静态变量值: " + staticVar);
}
}
2.2 加载(Loading)
目的:将字节码文件加载到内存中
过程:
- 通过类的全限定名获取字节码文件
- 将字节码文件转换为方法区的运行时数据结构
- 在内存中生成一个代表该类的 Class 对象
代码示例:
java
// 加载过程演示
public class LoadingDemo {
public static void main(String[] args) {
// 通过 Class.forName 显式加载类
try {
Class<?> clazz = Class.forName("java.lang.String");
System.out.println("类加载成功: " + clazz.getName());
System.out.println("类加载器: " + clazz.getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
2.3 验证(Verification)
目的:确保字节码文件的正确性和安全性
验证内容:
- 文件格式验证:验证字节码文件格式
- 元数据验证:验证类的元数据信息
- 字节码验证:验证字节码指令
- 符号引用验证:验证符号引用的正确性
验证示例:
java
// 验证过程演示
public class VerificationDemo {
public static void main(String[] args) {
// 尝试加载一个不存在的类
try {
Class<?> clazz = Class.forName("NonExistentClass");
} catch (ClassNotFoundException e) {
System.out.println("类不存在,验证失败: " + e.getMessage());
}
// 尝试加载一个格式错误的类
try {
Class<?> clazz = Class.forName("java.lang.String");
// 验证类的元数据
System.out.println("类名: " + clazz.getName());
System.out.println("父类: " + clazz.getSuperclass());
System.out.println("接口: " + Arrays.toString(clazz.getInterfaces()));
} catch (Exception e) {
System.out.println("验证失败: " + e.getMessage());
}
}
}
2.4 准备(Preparation)
目的:为类的静态变量分配内存并设置默认值
准备过程:
- 为静态变量分配内存
- 设置默认值(0、false、null)
- 为常量分配内存并设置值
准备示例:
java
// 准备阶段演示
public class PreparationDemo {
// 准备阶段:分配内存,设置默认值 0
private static int staticInt;
// 准备阶段:分配内存,设置默认值 false
private static boolean staticBoolean;
// 准备阶段:分配内存,设置默认值 null
private static String staticString;
// 准备阶段:分配内存,设置值 100(常量)
private static final int CONSTANT_INT = 100;
static {
// 初始化阶段:设置实际值
staticInt = 10;
staticBoolean = true;
staticString = "Hello";
}
public static void main(String[] args) {
System.out.println("静态变量值: " + staticInt);
System.out.println("静态布尔值: " + staticBoolean);
System.out.println("静态字符串: " + staticString);
System.out.println("常量值: " + CONSTANT_INT);
}
}
2.5 解析(Resolution)
目的:将符号引用转换为直接引用
解析内容:
- 类或接口解析:将类或接口的符号引用转换为直接引用
- 字段解析:将字段的符号引用转换为直接引用
- 方法解析:将方法的符号引用转换为直接引用
- 接口方法解析:将接口方法的符号引用转换为直接引用
解析示例:
java
// 解析过程演示
public class ResolutionDemo {
public static void main(String[] args) {
try {
// 类解析
Class<?> clazz = Class.forName("java.lang.String");
System.out.println("类解析成功: " + clazz.getName());
// 字段解析
Field field = clazz.getDeclaredField("value");
System.out.println("字段解析成功: " + field.getName());
// 方法解析
Method method = clazz.getMethod("length");
System.out.println("方法解析成功: " + method.getName());
// 接口方法解析
Class<?> listClass = Class.forName("java.util.List");
Method listMethod = listClass.getMethod("size");
System.out.println("接口方法解析成功: " + listMethod.getName());
} catch (Exception e) {
System.out.println("解析失败: " + e.getMessage());
}
}
}
2.6 初始化(Initialization)
目的:执行类的初始化代码
初始化过程:
- 执行静态代码块
- 为静态变量赋值
- 执行静态方法
初始化示例:
java
// 初始化过程演示
public class InitializationDemo {
// 静态变量
private static int staticVar = 10;
// 静态代码块
static {
System.out.println("静态代码块1执行");
staticVar = 20;
}
// 静态代码块
static {
System.out.println("静态代码块2执行");
staticVar = 30;
}
// 静态方法
public static void staticMethod() {
System.out.println("静态方法执行");
}
public static void main(String[] args) {
System.out.println("main 方法执行");
System.out.println("静态变量值: " + staticVar);
staticMethod();
}
}
2.7 使用(Using)
目的:类的正常使用阶段
使用过程:
- 创建类的实例
- 调用实例方法
- 访问实例变量
- 调用静态方法
- 访问静态变量
使用示例:
java
// 使用阶段演示
public class UsingDemo {
private int instanceVar = 100;
private static int staticVar = 200;
public UsingDemo() {
System.out.println("构造函数执行 - 使用阶段");
}
public void instanceMethod() {
System.out.println("实例方法执行 - 使用阶段");
System.out.println("实例变量值: " + instanceVar);
}
public static void staticMethod() {
System.out.println("静态方法执行 - 使用阶段");
System.out.println("静态变量值: " + staticVar);
}
public static void main(String[] args) {
System.out.println("=== 使用阶段演示 ===");
// 1. 调用静态方法
staticMethod();
// 2. 访问静态变量
System.out.println("访问静态变量: " + staticVar);
// 3. 创建实例
UsingDemo demo = new UsingDemo();
// 4. 调用实例方法
demo.instanceMethod();
// 5. 访问实例变量
System.out.println("访问实例变量: " + demo.instanceVar);
}
}
2.8 卸载(Unloading)
目的:从内存中移除不再使用的类
卸载条件:
- 该类的所有实例都已经被回收
- 加载该类的 ClassLoader 已经被回收
- 该类的 Class 对象没有被任何地方引用
卸载过程:
- 执行类的清理工作
- 释放类占用的内存
- 从方法区中移除类的信息
卸载示例:
java
// 卸载过程演示
public class UnloadingDemo {
static {
System.out.println("UnloadingDemo 类加载 - 初始化阶段");
}
public UnloadingDemo() {
System.out.println("UnloadingDemo 实例创建 - 使用阶段");
}
@Override
protected void finalize() throws Throwable {
System.out.println("UnloadingDemo 实例被回收 - 卸载阶段");
super.finalize();
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== 卸载过程演示 ===");
// 创建实例
UnloadingDemo demo = new UnloadingDemo();
// 使用实例
System.out.println("使用实例: " + demo);
// 释放引用
demo = null;
// 强制垃圾回收
System.gc();
// 等待垃圾回收完成
Thread.sleep(1000);
System.out.println("实例引用已释放,等待垃圾回收");
}
}
第三章:类加载器体系
3.1 类加载器层次结构
Bootstrap ClassLoader (启动类加载器)
↓
Extension ClassLoader (扩展类加载器)
↓
Application ClassLoader (应用程序类加载器)
↓
Custom ClassLoader (自定义类加载器)
3.2 启动类加载器(Bootstrap ClassLoader)
特点:
- 由 C++ 实现,是 JVM 的一部分
- 负责加载核心类库(rt.jar、charsets.jar 等)
- 是其他类加载器的父加载器
- 无法被 Java 程序直接引用
代码示例:
java
// 启动类加载器演示
public class BootstrapClassLoaderDemo {
public static void main(String[] args) {
// 获取核心类库的类加载器
Class<?> stringClass = String.class;
ClassLoader stringClassLoader = stringClass.getClassLoader();
System.out.println("String 类的类加载器: " + stringClassLoader);
System.out.println("String 类的类加载器是否为 null: " + (stringClassLoader == null));
// 获取其他核心类的类加载器
Class<?> objectClass = Object.class;
ClassLoader objectClassLoader = objectClass.getClassLoader();
System.out.println("Object 类的类加载器: " + objectClassLoader);
System.out.println("Object 类的类加载器是否为 null: " + (objectClassLoader == null));
}
}
3.3 扩展类加载器(Extension ClassLoader)
特点:
- 由 Java 实现
- 负责加载扩展类库(jre/lib/ext 目录下的 jar 包)
- 是应用程序类加载器的父加载器
代码示例:
java
// 扩展类加载器演示
public class ExtensionClassLoaderDemo {
public static void main(String[] args) {
// 获取扩展类加载器
ClassLoader extensionClassLoader = ClassLoader.getSystemClassLoader().getParent();
System.out.println("扩展类加载器: " + extensionClassLoader);
System.out.println("扩展类加载器类名: " + extensionClassLoader.getClass().getName());
// 获取扩展类加载器的父加载器
ClassLoader parent = extensionClassLoader.getParent();
System.out.println("扩展类加载器的父加载器: " + parent);
}
}
3.4 应用程序类加载器(Application ClassLoader)
特点:
- 由 Java 实现
- 负责加载应用程序类路径(classpath)下的类
- 是系统默认的类加载器
代码示例:
java
// 应用程序类加载器演示
public class ApplicationClassLoaderDemo {
public static void main(String[] args) {
// 获取应用程序类加载器
ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
System.out.println("应用程序类加载器: " + appClassLoader);
System.out.println("应用程序类加载器类名: " + appClassLoader.getClass().getName());
// 获取当前类的类加载器
ClassLoader currentClassLoader = ApplicationClassLoaderDemo.class.getClassLoader();
System.out.println("当前类的类加载器: " + currentClassLoader);
// 验证是否为同一个类加载器
System.out.println("是否为同一个类加载器: " + (appClassLoader == currentClassLoader));
}
}
第四章:双亲委派模型
4.1 双亲委派模型原理
双亲委派模型是类加载器的工作机制,确保类的唯一性和安全性。
工作流程:
- 当类加载器收到类加载请求时,首先委派给父加载器
- 如果父加载器无法加载,则由当前加载器加载
- 如果当前加载器也无法加载,则抛出 ClassNotFoundException
4.2 双亲委派模型实现
java
// 双亲委派模型演示
public class ParentDelegationDemo {
public static void main(String[] args) {
// 创建自定义类加载器
CustomClassLoader customLoader = new CustomClassLoader();
try {
// 尝试加载 String 类
Class<?> stringClass = customLoader.loadClass("java.lang.String");
System.out.println("String 类加载成功: " + stringClass.getName());
System.out.println("String 类的类加载器: " + stringClass.getClassLoader());
// 尝试加载自定义类
Class<?> customClass = customLoader.loadClass("ParentDelegationDemo");
System.out.println("自定义类加载成功: " + customClass.getName());
System.out.println("自定义类的类加载器: " + customClass.getClassLoader());
} catch (ClassNotFoundException e) {
System.out.println("类加载失败: " + e.getMessage());
}
}
}
// 自定义类加载器
class CustomClassLoader extends ClassLoader {
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
System.out.println("尝试加载类: " + name);
// 首先检查类是否已经被加载
Class<?> c = findLoadedClass(name);
if (c != null) {
System.out.println("类已加载: " + name);
return c;
}
try {
// 委派给父加载器
if (getParent() != null) {
System.out.println("委派给父加载器: " + name);
c = getParent().loadClass(name);
if (c != null) {
System.out.println("父加载器加载成功: " + name);
return c;
}
}
} catch (ClassNotFoundException e) {
System.out.println("父加载器无法加载: " + name);
}
// 当前加载器尝试加载
System.out.println("当前加载器尝试加载: " + name);
c = findClass(name);
if (c != null) {
System.out.println("当前加载器加载成功: " + name);
if (resolve) {
resolveClass(c);
}
return c;
}
throw new ClassNotFoundException(name);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 这里可以实现自定义的类加载逻辑
// 例如从网络、数据库等加载类
throw new ClassNotFoundException(name);
}
}
4.3 双亲委派模型的优势
- 类的唯一性:确保同一个类只被加载一次
- 安全性:防止核心类库被恶意替换
- 层次性:形成清晰的类加载器层次结构
4.4 双亲委派模型的破坏
破坏场景:
- SPI 机制:ServiceLoader 使用线程上下文类加载器
- OSGi 框架:使用网状类加载器结构
- 热部署:需要重新加载类
破坏示例:
java
// 双亲委派模型破坏示例
public class ParentDelegationBreakDemo {
public static void main(String[] args) {
// 使用线程上下文类加载器破坏双亲委派模型
ClassLoader originalLoader = Thread.currentThread().getContextClassLoader();
try {
// 设置自定义类加载器为上下文类加载器
CustomClassLoader customLoader = new CustomClassLoader();
Thread.currentThread().setContextClassLoader(customLoader);
// 使用 ServiceLoader 加载服务
ServiceLoader<Object> serviceLoader = ServiceLoader.load(Object.class);
System.out.println("ServiceLoader 使用上下文类加载器: " +
Thread.currentThread().getContextClassLoader());
} finally {
// 恢复原始类加载器
Thread.currentThread().setContextClassLoader(originalLoader);
}
}
}
第五章:类加载时机
5.1 类加载的触发条件
- 创建类的实例
- 访问类的静态变量
- 调用类的静态方法
- 使用反射访问类
- 初始化子类时,父类会被加载
- 虚拟机启动时,包含 main 方法的类会被加载
5.2 类加载时机演示
java
// 类加载时机演示
public class ClassLoadingTimingDemo {
public static void main(String[] args) {
System.out.println("=== 类加载时机演示 ===");
// 1. 创建类的实例
System.out.println("\n1. 创建类的实例");
new InstanceClass();
// 2. 访问类的静态变量
System.out.println("\n2. 访问类的静态变量");
System.out.println("静态变量值: " + StaticClass.staticVar);
// 3. 调用类的静态方法
System.out.println("\n3. 调用类的静态方法");
StaticClass.staticMethod();
// 4. 使用反射访问类
System.out.println("\n4. 使用反射访问类");
try {
Class<?> clazz = Class.forName("ReflectionClass");
System.out.println("反射加载类成功: " + clazz.getName());
} catch (ClassNotFoundException e) {
System.out.println("反射加载类失败: " + e.getMessage());
}
// 5. 初始化子类时,父类会被加载
System.out.println("\n5. 初始化子类时,父类会被加载");
new ChildClass();
}
}
// 实例类
class InstanceClass {
static {
System.out.println("InstanceClass 静态代码块执行");
}
public InstanceClass() {
System.out.println("InstanceClass 构造函数执行");
}
}
// 静态类
class StaticClass {
static {
System.out.println("StaticClass 静态代码块执行");
}
public static int staticVar = 10;
public static void staticMethod() {
System.out.println("StaticClass 静态方法执行");
}
}
// 反射类
class ReflectionClass {
static {
System.out.println("ReflectionClass 静态代码块执行");
}
}
// 父类
class ParentClass {
static {
System.out.println("ParentClass 静态代码块执行");
}
}
// 子类
class ChildClass extends ParentClass {
static {
System.out.println("ChildClass 静态代码块执行");
}
}
第六章:自定义类加载器
6.1 自定义类加载器实现
java
// 自定义类加载器实现
public class CustomClassLoader extends ClassLoader {
private String classPath;
public CustomClassLoader(String classPath) {
this.classPath = classPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
// 读取类文件
byte[] classData = loadClassData(name);
if (classData != null) {
// 定义类
return defineClass(name, classData, 0, classData.length);
}
} catch (IOException e) {
e.printStackTrace();
}
throw new ClassNotFoundException(name);
}
private byte[] loadClassData(String name) throws IOException {
String fileName = name.replace('.', '/') + ".class";
String fullPath = classPath + "/" + fileName;
try (FileInputStream fis = new FileInputStream(fullPath);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}
return baos.toByteArray();
}
}
}
6.2 自定义类加载器使用示例
java
// 自定义类加载器使用示例
public class CustomClassLoaderDemo {
public static void main(String[] args) {
try {
// 创建自定义类加载器
CustomClassLoader customLoader = new CustomClassLoader("src");
// 加载自定义类
Class<?> clazz = customLoader.loadClass("CustomClass");
System.out.println("类加载成功: " + clazz.getName());
System.out.println("类加载器: " + clazz.getClassLoader());
// 创建实例
Object instance = clazz.newInstance();
System.out.println("实例创建成功: " + instance);
} catch (Exception e) {
System.out.println("自定义类加载失败: " + e.getMessage());
e.printStackTrace();
}
}
}
// 自定义类
class CustomClass {
static {
System.out.println("CustomClass 静态代码块执行");
}
public CustomClass() {
System.out.println("CustomClass 构造函数执行");
}
public void sayHello() {
System.out.println("Hello from CustomClass!");
}
}
第七章:类加载实战演示
7.1 完整的类加载演示
java
// 完整的类加载演示
public class ClassLoadingCompleteDemo {
public static void main(String[] args) {
System.out.println("=== JVM 类加载机制完整演示 ===\n");
// 1. 类加载器层次结构演示
demonstrateClassLoaderHierarchy();
// 2. 双亲委派模型演示
demonstrateParentDelegation();
// 3. 类加载过程演示
demonstrateClassLoadingProcess();
// 4. 自定义类加载器演示
demonstrateCustomClassLoader();
// 5. 类加载时机演示
demonstrateClassLoadingTiming();
}
/**
* 演示类加载器层次结构
*/
private static void demonstrateClassLoaderHierarchy() {
System.out.println("=== 类加载器层次结构演示 ===");
// 获取当前类的类加载器
ClassLoader currentLoader = ClassLoadingCompleteDemo.class.getClassLoader();
System.out.println("当前类的类加载器: " + currentLoader);
// 获取父加载器
ClassLoader parent = currentLoader.getParent();
System.out.println("父加载器: " + parent);
// 获取祖父加载器
ClassLoader grandparent = parent.getParent();
System.out.println("祖父加载器: " + grandparent);
// 获取核心类的类加载器
ClassLoader stringLoader = String.class.getClassLoader();
System.out.println("String 类的类加载器: " + stringLoader);
System.out.println();
}
/**
* 演示双亲委派模型
*/
private static void demonstrateParentDelegation() {
System.out.println("=== 双亲委派模型演示 ===");
try {
// 尝试加载核心类
Class<?> stringClass = Class.forName("java.lang.String");
System.out.println("String 类加载成功: " + stringClass.getName());
System.out.println("String 类的类加载器: " + stringClass.getClassLoader());
// 尝试加载扩展类
Class<?> listClass = Class.forName("java.util.List");
System.out.println("List 接口加载成功: " + listClass.getName());
System.out.println("List 接口的类加载器: " + listClass.getClassLoader());
} catch (ClassNotFoundException e) {
System.out.println("类加载失败: " + e.getMessage());
}
System.out.println();
}
/**
* 演示类加载过程
*/
private static void demonstrateClassLoadingProcess() {
System.out.println("=== 类加载过程演示 ===");
try {
// 加载类
Class<?> clazz = Class.forName("ClassLoadingProcessDemo");
System.out.println("类加载成功: " + clazz.getName());
// 创建实例
Object instance = clazz.newInstance();
System.out.println("实例创建成功: " + instance);
} catch (Exception e) {
System.out.println("类加载过程演示失败: " + e.getMessage());
}
System.out.println();
}
/**
* 演示自定义类加载器
*/
private static void demonstrateCustomClassLoader() {
System.out.println("=== 自定义类加载器演示 ===");
try {
// 创建自定义类加载器
CustomClassLoader customLoader = new CustomClassLoader("src");
// 加载类
Class<?> clazz = customLoader.loadClass("CustomClass");
System.out.println("自定义类加载成功: " + clazz.getName());
System.out.println("自定义类的类加载器: " + clazz.getClassLoader());
} catch (Exception e) {
System.out.println("自定义类加载器演示失败: " + e.getMessage());
}
System.out.println();
}
/**
* 演示类加载时机
*/
private static void demonstrateClassLoadingTiming() {
System.out.println("=== 类加载时机演示 ===");
// 访问静态变量
System.out.println("访问静态变量: " + TimingDemo.staticVar);
// 调用静态方法
TimingDemo.staticMethod();
// 创建实例
new TimingDemo();
System.out.println();
}
}
// 类加载过程演示类
class ClassLoadingProcessDemo {
static {
System.out.println("ClassLoadingProcessDemo 静态代码块执行");
}
public ClassLoadingProcessDemo() {
System.out.println("ClassLoadingProcessDemo 构造函数执行");
}
}
// 类加载时机演示类
class TimingDemo {
static {
System.out.println("TimingDemo 静态代码块执行");
}
public static int staticVar = 100;
public static void staticMethod() {
System.out.println("TimingDemo 静态方法执行");
}
public TimingDemo() {
System.out.println("TimingDemo 构造函数执行");
}
}
总结
JVM 类加载机制关键要点
- 类加载完整生命周期:加载 → 验证 → 准备 → 解析 → 初始化 → 使用 → 卸载
- 类加载器体系:启动类加载器 → 扩展类加载器 → 应用程序类加载器 → 自定义类加载器
- 双亲委派模型:确保类的唯一性和安全性
- 类加载时机:创建实例、访问静态成员、反射、初始化子类等
- 使用阶段:类的正常使用,包括实例创建、方法调用、变量访问等
- 卸载阶段:类的内存回收,需要满足三个条件才能被卸载
- 自定义类加载器:实现热部署、类隔离等功能
类加载机制的重要性
- 安全性:防止核心类库被恶意替换
- 唯一性:确保同一个类只被加载一次
- 灵活性:支持自定义类加载逻辑
- 性能:通过缓存和委派机制提高加载效率
通过深入理解 JVM 类加载机制,我们可以更好地理解 Java 程序的运行原理,也能在需要时实现自定义的类加载逻辑。