1 Star 2 Fork 2

MyCoder4j/coder4j

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
detail
Java-AQS.md
Java-CAS.md
Java-CountDownLatch.md
Java-CyclicBarrier.md
Java-HashMap.md
Java-JVM内存模型详解.md
Java-Semaphore.md
Java-Thread.md
Java-Unsafe.md
Java-abstract.md
Java-final.md
Java-java.util.concurrent包.md
Java-static详解.md
Java-synchronized详解.md
Java-volatile关键字.md
Java-反射机制.md
Java-类加载机制.md
Java-线程安全.md
Java-线程池.md
Java-设计模式.md
Java-集合框架.md
Spring-AOP.md
Spring-Bean.md
Spring-DI.md
Spring-事务管理机制.md
Spring-数据访问机制.md
img
.gitignore
README.md
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
Java-类加载机制.md 7.35 KB
一键复制 编辑 原始数据 按行查看 历史
lanling 提交于 7个月前 . 添加文档

[TOC]

Java 类加载机制详解

Java 类加载机制是 Java 运行时环境的一个重要组成部分,负责将类文件加载到 JVM 中并进行验证、准备、解析和初始化。类加载机制的设计思路和实现原理保证了 Java 程序的安全性和灵活性。

设计思路

  1. 模块化:类加载机制将类的加载过程分为多个阶段,每个阶段由不同的类加载器负责,实现了模块化和层次化的管理。
  2. 双亲委派模型:类加载器之间采用双亲委派模型,确保类的加载具有全局唯一性,避免类的重复加载。
  3. 安全性:通过类加载机制,可以控制类的加载和访问,增强系统的安全性。
  4. 灵活性:允许开发者自定义类加载器,满足特定需求。

Gitee地址:

实现原理

Java 类加载机制主要分为以下几个阶段:

  1. 加载(Loading)

    • 将类的字节码文件从文件系统、网络或其他来源读取到内存中。
    • 将字节码数据转换成 java.lang.Class 对象。
    • 生成类的二进制数据表示。
  2. 验证(Verification)

    • 确保加载的类文件格式正确,没有被篡改。
    • 检查类的结构是否符合 JVM 规范。
    • 验证类的字节码指令是否合法。
  3. 准备(Preparation)

    • 为类的静态变量分配内存,并设置默认初始值(如 int 类型的默认值为 0)。
    • 不会执行初始化块或构造方法。
  4. 解析(Resolution)

    • 将类、接口、字段和方法的符号引用转换为直接引用。
    • 符号引用是以文本形式存在的,而直接引用是直接指向目标的指针或偏移量。
  5. 初始化(Initialization)

    • 执行类的初始化代码,包括静态初始化块和静态变量的赋值。
    • 按照一定的顺序执行初始化代码。

类加载器

Java 类加载器主要有以下几种:

  1. 启动类加载器(Bootstrap ClassLoader)

    • 负责加载 JDK 的核心类库,如 rt.jar 中的类。
    • 由 C++ 实现,不是 java.lang.ClassLoader 的子类。
  2. 扩展类加载器(Extension ClassLoader)

    • 负责加载 Java 的扩展类库,如 jre/lib/ext 目录下的类。
    • java.lang.ClassLoader 的子类。
  3. 应用类加载器(Application ClassLoader)

    • 负责加载应用程序类路径(CLASSPATH)下的类。
    • 也是 java.lang.ClassLoader 的子类。
  4. 自定义类加载器

    • 开发者可以根据需要自定义类加载器,继承 java.lang.ClassLoader 并重写 findClassloadClass 方法。

双亲委派模型

双亲委派模型是类加载器之间的层次关系,其工作流程如下:

  1. 委托给父类加载器

    • 当一个类加载器收到类加载请求时,首先将请求委托给父类加载器。
    • 父类加载器再委托给它的父类加载器,直到最顶层的启动类加载器。
  2. 父类加载器加载

    • 如果父类加载器能够完成类的加载,则返回加载的类。
    • 否则,子类加载器尝试自己加载该类。
  3. 子类加载器加载

    • 如果父类加载器无法加载类,则子类加载器尝试加载类。
    • 子类加载器通过 findClass 方法找到类的字节码并加载。

应用场景

  1. Web 应用服务器

    • 如 Tomcat、Jetty 等,通过自定义类加载器加载 Web 应用中的类,实现热部署和类隔离。
  2. OSGi

    • OSGi 框架通过自定义类加载器实现模块化和动态更新。
  3. 插件系统

    • 通过自定义类加载器动态加载和卸载插件,实现插件的热插拔。
  4. 安全沙箱

    • 通过自定义类加载器限制某些类的加载,实现安全沙箱环境。

注意事项

  1. 类加载顺序

    • 类加载器按照双亲委派模型的顺序加载类,确保类的加载具有全局唯一性。
  2. 类加载器的可见性

    • 子类加载器可以访问父类加载器加载的类,但父类加载器不能访问子类加载器加载的类。
  3. 类的唯一性

    • 同一个类在同一个类加载器下只能被加载一次,避免类的重复加载。
  4. 类加载器的线程安全

    • 类加载器的 loadClass 方法是线程安全的,但自定义类加载器需要注意线程安全问题。
  5. 类加载器的资源释放

    • 类加载器加载的类占用的内存不会被垃圾回收,除非类加载器本身被垃圾回收。

破坏类加载机制的情况

  1. 类加载器的错误实现

    • 自定义类加载器如果错误地实现了 loadClassfindClass 方法,可能导致类加载失败或类的重复加载。
  2. 类路径冲突

    • 如果类路径中存在同名类,可能导致类加载器加载错误的类。
  3. 类加载器的隔离性问题

    • 在多模块或多应用环境中,如果类加载器的隔离性设计不当,可能导致类的冲突或加载失败。
  4. 类加载器的循环依赖

    • 如果类加载器之间存在循环依赖,可能导致类加载死锁或无限递归。
  5. 类加载器的内存泄漏

    • 如果类加载器加载的类长时间不被卸载,可能导致内存泄漏。

示例代码

自定义类加载器
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class MyClassLoader extends ClassLoader {
    private String classPath;

    public MyClassLoader(String classPath) {
        this.classPath = classPath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = loadClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name, classData, 0, classData.length);
        }
    }

    private byte[] loadClassData(String className) {
        String path = classPath + "/" + className.replace(".", "/") + ".class";
        try (InputStream is = getClass().getResourceAsStream(path);
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            int len = 0;
            while ((len = is.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        MyClassLoader loader = new MyClassLoader("path/to/classes");
        try {
            Class<?> clazz = loader.loadClass("com.example.MyClass");
            Object obj = clazz.newInstance();
            System.out.println("Object created: " + obj);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

总结

Java 类加载机制是 Java 运行时环境的核心部分,通过模块化、双亲委派模型和安全性设计,确保了类的加载具有全局唯一性和安全性。类加载器的层次结构和自定义类加载器的应用使得 Java 程序具有高度的灵活性和扩展性。理解类加载机制的实现原理和注意事项,有助于开发者更好地管理和优化 Java 应用程序的类加载过程。

Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/MyCoder4j/coder4j.git
git@gitee.com:MyCoder4j/coder4j.git
MyCoder4j
coder4j
coder4j
master

搜索帮助