jvm基础三——类加载器

类加载器

在Java中,类加载器(Class Loader)是Java虚拟机(JVM)的一部分,负责将类文件(.class文件)加载到JVM中,使得程序能够使用这些类。类加载器在Java中具有重要的作用,它的主要任务包括:

  1. 加载(Loading):找到并加载类文件的字节码数据。类加载器根据类的全限定名(Fully Qualified Name)来查找并读取对应的类文件。

  2. 链接(Linking):链接包括验证、准备和解析这三个步骤。

    • 验证(Verification):确保类文件的字节码符合JVM规范,并且安全地加载到JVM中。
    • 准备(Preparation):为类的静态变量分配内存,并设置默认初始值。
    • 解析(Resolution):将符号引用转换为直接引用。
  3. 初始化(Initialization):对类进行初始化,包括执行静态代码块和初始化静态变量等。

Java的类加载器采用了双亲委派模型(Parent Delegation Model)。根据这个模型,当需要加载一个类时,类加载器首先会委派给父类加载器加载,只有在父类加载器无法加载该类时,才会尝试自己加载。这种层次化的类加载器体系保证了类的统一性和安全性,同时也避免了类的重复加载。

Java中的类加载器可以分为以下几种类型:

  1. Bootstrap Class Loader (引导类加载器):是JVM的一部分,它负责加载JVM自身需要的类,包括java.lang包中的类。它是用本地代码(Native Code)实现的,无法直接在Java中获取对其的引用。

  2. Extension Class Loader (扩展类加载器):负责加载Java的扩展库,位于JRE的lib/ext目录下的类。它是由sun.misc.Launcher$ExtClassLoader类实现的,是Bootstrap Class Loader的子类。

  3. Application Class Loader (应用程序类加载器):也叫系统类加载器,负责加载应用程序中的类。它是由sun.misc.Launcher$AppClassLoader类实现的,是Extension Class Loader的子类。

  4. 自定义类加载器 :Java允许用户自定义类加载器,继承自java.lang.ClassLoader类,实现自定义的加载逻辑。通过自定义类加载器,可以实现一些特殊的类加载需求,比如从网络、数据库或其他非标准位置加载类。

Java的类加载器机制为Java程序提供了灵活性和安全性,可以根据不同的需求扩展或自定义类加载器,实现各种复杂的类加载逻辑

类加载器的双亲委派机制

Java中的类加载器采用了双亲委派机制(Parent Delegation Model),这是一种类加载器的工作原则,用于保证类加载的统一性和安全性。该机制基于一个简单的原则:除非父类加载器无法加载该类,否则由父类加载器加载。

下面是双亲委派机制的工作流程:

  1. 当一个类加载器收到加载类的请求时,它首先不会自己尝试去加载这个类,而是把请求委托给父类加载器去完成。

  2. 每个类加载器都会把加载请求向上委托给父类加载器,直到达到顶层的引导类加载器(Bootstrap Class Loader)。

  3. 如果父类加载器可以加载这个类,就成功返回;如果父类加载器无法加载,子类加载器才会尝试自己去加载这个类。

这种机制的优势在于确保了Java核心库的一致性:无论哪个类加载器加载一个类,最终被加载的类都是相同的。这样可以避免在不同的类加载器下出现同名类的冲突问题。

双亲委派机制还提高了安全性。因为在这种机制下,Java类的加载都是从根加载器开始的,根加载器只加载标准的核心Java类库,不会加载应用程序的类。这样可以防止应用程序通过替换核心Java类库中的类来破坏JVM的稳定性和安全性。

总的来说,双亲委派机制保证了类加载的一致性、安全性和稳定性,是Java类加载机制的核心之一。

打破

尽管双亲委派机制在大多数情况下都是非常有用的,但在某些特殊情况下,可能需要打破双亲委派机制。打破双亲委派机制通常是为了实现一些特殊的类加载需求,比如热部署、动态更新等。

在Java中,要打破双亲委派机制,一般需要自定义类加载器,并重写其加载类的方法。下面是一种可能的方法:

  1. 自定义类加载器:继承自 ClassLoader 类,并重写 loadClass() 方法

  2. loadClass() 方法中,根据需要的加载策略,决定是否调用父类加载器的 loadClass() 方法。

  3. 如果需要打破双亲委派机制,可以在自定义类加载器的 loadClass() 方法中直接加载指定类,而不是委托给父类加载器。

下面是一个简单的示例代码,演示如何打破双亲委派机制:

public class MyClassLoader extends ClassLoader {

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if (name.startsWith("com.example")) {
            // 对于指定的类,直接由自定义类加载器加载
            return findClass(name);
        }
        // 其他类委托给父类加载器加载
        return super.loadClass(name);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // 实现自定义的类加载逻辑,比如从文件或网络加载类的字节码数据
        // 这里只是一个简单示例,实际应用中需要根据具体需求实现
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException(name);
        }
        return defineClass(name, classData, 0, classData.length);
    }

    // 实现获取类字节码数据的方法,这里只是一个简单示例,实际应用中需要根据具体需求实现
    private byte[] getClassData(String name) {
        // 从指定的位置获取类字节码数据
        // 这里可以是文件、网络等来源
        return null;
    }
}

在上面的示例中,自定义了一个 MyClassLoader 类继承自 ClassLoader,重写了 loadClass() 方法和 findClass() 方法。在 loadClass() 方法中,指定了对于以 "com.example" 开头的类,直接由自定义类加载器加载,而对于其他类,委托给父类加载器加载。在 findClass() 方法中,实现了加载类的具体逻辑,可以从指定的位置获取类的字节码数据并定义类。

需要注意的是,打破双亲委派机制可能会导致类加载冲突和安全问题,因此应谨慎使用,并确保了解其潜在的影响。

jdk8后类加载器(模块化)

总结

相关推荐
ღ᭄ꦿ࿐Never say never꧂3 分钟前
Redis 五大基本数据类型及其应用场景进阶(缓存预热、雪崩 、穿透 、击穿)
java·数据库·redis·缓存预热、雪崩、击穿、穿透·五大基本数据类型
It'sMyGo6 分钟前
javascript手写实现instanceof函数 介绍判断数组的方法
开发语言·javascript·原型模式
程序员阿鹏30 分钟前
HashMap为什么线程不安全?如何实现线程安全
java·开发语言·数据结构·安全·eclipse·intellij-idea
m0_675447081 小时前
Java ERP管理系统源码解析:微服务架构实践Spring Cloud Alibaba与Spring Boot
java·erp管理系统源码
猫武士水星1 小时前
分糖果C++
开发语言·c++
计算机学姐1 小时前
基于SpringBoot+Vue的留学信息推荐系统
java·vue.js·spring boot·后端·mysql·spring·mybatis
NMBG221 小时前
[数据结构] 二叉树题目(一)
java·数据结构·算法·leetcode
HHoao1 小时前
Java 安全认证和 Hadoop UGI 原理解析
java·hadoop·安全
像风一样的男人@2 小时前
python --qt5(webview)/防多开/套壳网页/多次点击激活旧窗口
开发语言·python·qt
_.Switch2 小时前
Python机器学习:数据预处理与清洗的打开方式
开发语言·人工智能·python·深度学习·机器学习·oracle·架构