JVM专题五:类加载器与双亲委派机制

news2024/11/24 16:55:52

通过上一篇Java的类加载机制相信大家已经搞明白了整个类加载从触发时机,接着我们就来看下类加载器,因为类加载机制是有加载器实现的。 

类加载器的分类

启动类加载器

Bootstrap ClassLoader 是 Java 虚拟机(JVM)的一部分,它负责加载 Java 核心库,也就是 Java Runtime Environment (JRE) 中的类。这些类通常位于 JRE 的 lib 目录下的 rt.jar 文件中,以及可能的其他 JAR 文件,比如 jsse.jar(Java Secure Socket Extension)等。

Bootstrap ClassLoader 是一个虚拟的类加载器,它不继承自 java.lang.ClassLoader,也不可以被直接引用或实例化。它是 JVM 的一部分,并且是所有类加载器的父加载器。当 JVM 启动时,Bootstrap ClassLoader 首先加载 rt.jar 中的类,然后这些类可以被其他类加载器使用。

Bootstrap ClassLoader 的主要作用包括:

  1. 加载 Java 核心库,如 java.lang 包中的类。
  2. 作为类加载器层次结构的根,为其他类加载器提供基础。

扩展类加载器

Extension ClassLoader 是 Java 虚拟机(JVM)中的一个系统类加载器,它负责加载 Java 扩展目录中的类库。以下是关于 Extension ClassLoader 的几个关键点:

  1. 加载职责:Extension ClassLoader 专门用来加载 lib/ext 目录或者由系统属性 java.ext.dirs 指定的其他目录中的类库。这些类库通常包括 Java 平台的标准扩展,例如一些常用的第三方库。

  2. 类加载顺序:在 JVM 的类加载体系中,Extension ClassLoader 位于 Bootstrap ClassLoader 之后。当一个类请求被提交到 JVM 时,Bootstrap ClassLoader 首先尝试加载,如果找不到相应的类,请求会传递给 Extension ClassLoader。

  3. 双亲委派模型:Extension ClassLoader 遵循 Java 的双亲委派模型,这意味着在尝试自己加载类之前,它会先委托给父类加载器(在这个情况下是 Bootstrap ClassLoader)进行加载。

  4. 配置灵活性:开发者可以通过设置 java.ext.dirs 系统属性来指定多个扩展目录,使得 JVM 可以在启动时从这些目录中加载类库。

应用类加载器

Application ClassLoader 是 JVM 中的一个系统类加载器,它的作用是加载应用程序类路径(ClassPath)上指定的类库。以下是 Application ClassLoader 的几个关键点:

1. 加载职责:Application ClassLoader 主要负责加载用户编写的 Java 应用程序代码。这些代码通常位于项目的 `bin` 或 `classes` 目录下,或者是通过 JAR 文件提供的。

2. 类加载顺序:在 JVM 的类加载器层级中,Application ClassLoader 位于 Extension ClassLoader 之后。这意味着,如果一个类同时在扩展目录和应用程序类路径中存在,Extension ClassLoader 会优先加载它。

3. 双亲委派模型:和 Extension ClassLoader 一样,Application ClassLoader 也遵循双亲委派模型。当它需要加载一个类时,会先委托给父类加载器(Extension ClassLoader)尝试加载,如果父类加载器无法加载,Application ClassLoader 才会尝试从应用程序类路径加载。

4. 灵活性:开发者可以通过设置系统属性 `java.class.path` 或使用 `-cp` 或 `-classpath` 命令行选项来指定应用程序类路径,从而控制 Application ClassLoader 加载的类库。

简而言之,Application ClassLoader 是我们程序员在 Java 应用程序开发过程中接触最多的类加载器,它负责将编写的 Java 类加载到 JVM 中,是 Java 应用程序运行的基础。其实你大致就理解为去加载你写好的Java代码,这个类加载器就负责加载你写好的那些类到内存里

自定义类加载器

自定义类加载器是 Java 动态加载类的一种强大机制,它允许开发者根据特定的需求来加载类。这种机制特别有用在需要动态加载或更新类定义的场景中,例如在热部署、模块化应用、或者需要从非标准源加载类文件等情况下。以下是 自定义类加载器的几个关键点:

  1. 继承性:自定义类加载器需要继承 java.lang.ClassLoader 类,并重写 findClass 方法来实现自定义的类查找逻辑。

  2. 加载逻辑:开发者可以在 findClass 方法中实现自己的类加载逻辑,例如从数据库、网络、文件系统等非标准位置加载类文件。

  3. 双亲委派模型:自定义类加载器同样遵循 Java 的双亲委派模型。在尝试加载类之前,它会委托给父类加载器,如果父类加载器无法加载,自定义类加载器才会介入。

  4. 隔离性:自定义类加载器可以创建与系统类加载器和应用程序类加载器隔离的类,这有助于实现模块化和防止类冲突。

  5. 安全性:自定义类加载器可以提供额外的安全检查,例如验证类文件的来源或内容,确保加载的类是安全的。

  6. 灵活性:通过自定义类加载器,开发者可以控制类的加载时机、来源和方式,为 Java 应用程序提供更高的灵活性和可扩展性。

  7. 使用场景:自定义类加载器适用于需要动态加载类、实现类版本控制、或者需要加载特定格式类文件的应用程序。

        通过自定义类加载器,Java 应用程序可以突破传统的类加载限制,实现更加灵活和动态的类加载策略。这对于需要高度定制化和模块化的应用程序尤其重要。下面提供简单的代码实例:

import java.io.*;
import java.nio.file.*;
import java.util.logging.*;

public class MyClassLoader extends ClassLoader {

    private static final Logger LOGGER = Logger.getLogger(MyClassLoader.class.getName());

    private final String classPath;
    private final boolean verify;

    public MyClassLoader(String classPath) {
        this(classPath, true);
    }

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

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String fileName = name.replace('.', '/') + ".class";
        byte[] classData = loadClassData(fileName);
        if (classData == null) {
            throw new ClassNotFoundException("Could not find " + fileName + " in " + classPath);
        }
        if (verify) {
            verifyClassData(classData);
        }
        return defineClass(name, classData, 0, classData.length);
    }

    private byte[] loadClassData(String fileName) throws IOException {
        Path path = Paths.get(classPath, fileName);
        try (InputStream in = Files.newInputStream(path)) {
            return in.readAllBytes();
        }
    }

    private void verifyClassData(byte[] classData) {
        // 这里可以添加对类数据的验证逻辑
        LOGGER.info("Class data verification is not implemented yet.");
    }

    public static void main(String[] args) {
        try {
            String classPath = "path/to/your/classes"; // 替换为实际的类文件路径
            MyClassLoader myClassLoader = new MyClassLoader(classPath);

            Class<?> myClass = myClassLoader.loadClass("com.example.MyClass");
            Object instance = myClass.getDeclaredConstructor().newInstance();

            // 假设 MyClass 有一个 sayHello 方法
            java.lang.reflect.Method sayHelloMethod = myClass.getMethod("sayHello");
            sayHelloMethod.invoke(instance);
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "Error loading class", e);
        }
    }
}

双亲委派机制

双亲委派机制层级结构

JVM的类加载器是有亲子层级结构的,就是说启动类加载器是最上层的,扩展类加载器在第二层,第三层是应用程序类加载器,最后一层是自定义类加载器,虽然说是最后一层,但是因为我们可以无限自定义,所以其实就是树的结构,深度可以不断累加。如下图:

为什么需要双亲委派机制?

双亲委派机制是 Java 类加载机制的核心原则之一,其设计具有以下几个重要目的:

  1. 安全性:双亲委派机制确保了 Java 核心库的类不能被随意替换或篡改。因为只有引导类加载器(Bootstrap ClassLoader)才能加载 rt.jar 中的 Java 核心类库,而它是不可扩展的。这防止了恶意代码对 Java 核心库的破坏。

  2. 避免类的重复加载:通过委托给父类加载器,JVM 确保了一个类在 JVM 内只被加载一次。这避免了类的重复加载,节省了内存资源,并且保证了类的唯一性。

  3. 保证加载顺序:双亲委派模型保证了类按照既定的顺序加载,例如,Java 核心库总是首先加载,然后是扩展库,最后是应用程序类。这有助于维护程序的稳定性和可预测性。

  4. 封装性和层次性:双亲委派模型允许 Java 应用程序分层,每一层都有自己的类加载器。这有助于实现模块化,使得不同层次的代码可以独立地更新和替换,而不会影响到其他层次。

  5. 类空间隔离:在复杂的应用程序中,如 Web 容器或 OSGi 框架,双亲委派模型通过使用不同的类加载器来实现类空间的隔离。这使得不同的模块或应用可以加载相同类的不同版本,而不会相互冲突。

  6. 易于实现和维护:双亲委派模型的实现相对简单,逻辑清晰,易于理解和维护。开发者可以专注于实现自己的 findClass 方法,而不必担心类加载的委托逻辑。

  7. 灵活性:虽然双亲委派模型是 Java 类加载的默认策略,但它并不是强制性的。开发者可以通过自定义类加载器来实现特定的类加载逻辑,例如,从数据库或网络加载类。

  8. 优化性能:通过缓存已加载的类(在 ClassLoader 中的 classes 集合中),JVM 可以快速检查类是否已经被加载,从而避免不必要的加载过程,提高性能。

简而言之,双亲委派机制为 Java 类加载提供了一种安全、有效、可预测的策略,有助于维护 Java 应用程序的稳定性和性能。

Tomcat真的破双亲委派机制了嘛?

Tomcat 并没有打破 Java 的双亲委派模型,而是在双亲委派模型的基础上进行了适应 Web 容器需求的扩展。下面是几个关键点来说明这一点:

  1. 双亲委派模型的核心:双亲委派模型的核心是,当一个类加载器收到类加载请求时,它会先委托给父类加载器去尝试加载这个类,直到达到启动类加载器。如果父类加载器无法完成加载任务,才会由子类加载器尝试加载。

  2. Tomcat 类加载器的实现:Tomcat 实现了多个层次的类加载器,包括 Common ClassLoader、Server ClassLoader、Shared ClassLoader 和 Webapp ClassLoader。这些类加载器都遵循双亲委派模型的委派逻辑。

  3. Webapp ClassLoader 的特殊性:虽然 Tomcat 的 Webapp ClassLoader 允许 Web 应用加载自己版本的类,但这是通过实现自己的 findClass 方法来实现的,而不是通过重写 loadClass 方法来绕过双亲委派模型。Webapp ClassLoader 仍然会首先委托父类加载器尝试加载类。

  4. 线程上下文类加载器(TCCL):Tomcat 在执行请求时,会将当前 Web 应用的类加载器设置为 TCCL。这确保了请求处理过程中加载的类首先会使用 Webapp ClassLoader,但这个过程仍然是在双亲委派模型的框架内进行的。

  5. 隔离性和灵活性:Tomcat 的类加载器设计提供了类隔离和版本控制的灵活性,这并不违反双亲委派模型,而是扩展了类加载器的使用场景。

类加载器实际的关系

        双亲委派机制,最早我一直以为是子类继承父类,其实看到源码以后发现实际上并没有继承关系,而是有成员变量取名叫parent,所以‘亲’由此而来;他们之间是组合关系并非是继承嗷!

        说到这里,本来该结束了,突然想到Effective Java中有一条原则是:组合优于继承?有小伙伴知道吗?哈哈这里就不展开了,后续会在设计模式模块好好唠唠。

结合上述类加载器,回头看看类加载机制忘了的小伙伴可以看上篇文章Java的类加载机制,整体流程如下图:

现在我们已经了解到了如何把文件加载到JVM里了,那从JVM角度考虑这些类的数据应该存放到什么位置呢?运行时候产生的动态数据又该放到哪里呢?

 专题汇总

JVM专题一:深入分析Java工作机制

JVM专题二:Java如何进行编译的

JVM专题三:Java代码如何运行

JVM专题四:JVM的类加载机制

JVM专题五:类加载器与双亲委派机制

JVM专题六:JVM的内存模型

JVM专题七:JVM垃圾回收机制

JVM专题八:JVM如何判断可回收对象

JVM专题九:JVM分代知识点梳理

JVM专题十:JVM中的垃圾回收机制

JVM专题十一:JVM 中的收集器一

JVM专题十二:JVM 中的收集器二

JVM专题十三:总结与整理(面试常用)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1872596.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Sqlserver双活

要实现Sqlserver双活不是一件简单的事情&#xff0c;什么是双活&#xff0c;就是两边都活着&#xff0c;两边都可以访问&#xff0c;也就是A服务器部署一个sqlserver服务&#xff0c;B服务器部署一个sqlserver服务&#xff0c;两边数据双向同步保持一致&#xff0c;当A数据库服…

web前端之文档流、浮动、定位详解

目录 一、文档流 二、浮动 1.添加浮动 2.清除浮动 三、定位 1.相对定位 2.绝对定位 一、文档流 什么是文档流&#xff1f; ● 文档流指的是文档中的标签在排列时所占用的位置。 将窗体自上而下分成一行行 &#xff0c;并在每 行中按从左至右的顺序排放标签&#xff0c…

智慧校园-学工管理系统总体概述

智慧校园学工管理系统是高等教育机构内部管理不可或缺的一部分&#xff0c;它通过集成信息技术&#xff0c;全面覆盖学生从入学至毕业的各类事务&#xff0c;旨在优化学生工作流程&#xff0c;强化管理效率&#xff0c;同时深化学生与学校间的互动&#xff0c;确保学生需求得到…

【红帽战报】6月RHCE考试喜报!

往期战报回顾&#xff1a; 点击查看【战报】5月RHCE考试喜报&#xff01;通过率100% 点击查看【战报】4月份红帽考试战报&#xff01; 点击查看【战报】PASS&#xff01;PASS&#xff01;2023年终来一波RHCE考试 微思网络-红帽官方授权合作伙伴&#xff01;面向全国招生&…

【华为战报】5月、6月HCIP考试战报!

华为认证&#xff1a;HCIA-HCIP-HCIE 点击查看&#xff1a; 【华为战报】4月 HCIP考试战报&#xff01; 【华为战报】2月、3月HCIP考试战报&#xff01; 【华为战报】11月份HCIP考试战报&#xff01; 【HCIE喜报】HCIE备考2个月丝滑通关&#xff0c;考试心得分享&#xff…

项目管理中常见的6种度量指标,你知道吗?

在项目管理中&#xff0c;为了有效地监控和控制项目的进展、成本、质量等方面&#xff0c;我们通常会采用一系列的度量指标。这些度量指标不仅可以帮助项目经理了解项目的当前状态&#xff0c;还能预测未来的趋势&#xff0c;从而作出相应的决策。以下是六种常见的项目度量数据…

基于大模型的Agent进行任务规划的10种方式(附代码和论文)

在 OpenAI AI 应用研究主管 Lilian Weng 的博客**《大语言模型&#xff08;LLM&#xff09;支持的自主式代理》**[1]中&#xff0c;将规划能力视为关键的组件之一&#xff0c;用于将任务拆解为更小可管理的子任务&#xff0c;这对有效可控的处理好更复杂的任务效果显著。 基于…

自适应蚁群算法优化的攀爬机器人的路径规划

大家好&#xff0c;我是带我去滑雪&#xff01; 攀爬机器人是一种能够在复杂环境中自主移动和攀爬的具有广阔应用前景的智能机器人&#xff0c;具有较强的应用潜力和广泛的研究价值。随着科技的不断发展&#xff0c;攀爬机器人在许多领域中的应用越来越广泛&#xff0c;例如建筑…

Python 面试【初级】

阐述以下方法 classmethod, staticmethod, property&#xff1f; 解释什么是lambda函数&#xff1f;它有什么好处&#xff1f;

phpMyAdmin 4.0.10 文件包含 -> getshell

phpMyAdmin 4.0.10 文件包含 -> getshell 前言&#xff1a;这里这个漏洞相对来说审计起来不是特别难&#xff0c;但是对于初学者还是有点挑战性的&#xff0c;从zkaq web课过来的小伙伴想挑战一下自己代码审计能力的话&#xff0c;可以直接跳到最后下载源码&#xff0c;聂风…

一键进阶ComfyUI!懂AI的设计师现在都在用的节点式Stable Diffusion

前言 _ 万字教程&#xff01;奶奶看了都会的 ComfyUI 入门教程 推荐阅读 一、川言川语 大家好&#xff0c;我是言川。 阅读文章 > ](https://www.uisdc.com/comfyui-3) 目前使用 Stable Diffusion 进行创作的工具主要有两个&#xff1a;WebUI 和 ComfyUI。而更晚出现的…

顶顶通呼叫中心中间件-透传uuid并且导入对端变量到本端(mod_cti基于Freeswitch)

一、配置拨号方案 win-ccadmin配置方法 点击拨号方案 -> 点击进入排队 -> 根据图中配置。如果不是排队转人工是机器人转人工那么就是在机器人那个拨号方案配置&#xff0c;并且需要配置在"cti_robot"之前即可 action"set" data"sip_h_X_tas…

【HTML03】HTML表单语法笔记,附带案例-作业

文章目录 表单概述一、表单容器&#xff08;form&#xff09;二、控件相关单词获取本次课程作业和案例 表单概述 允许用户输入信息&#xff0c;和提交信息的-收集用户信息。 表单&#xff1a;表单容器表单控件组成。 控件&#xff1a;输入框、单选按钮、多选、下拉框、多行文…

72V转12V非隔离DC/DC电源原理图+PCB源文件

资料下载地址&#xff1a;72V转12V非隔离DCDC电源原理图PCB源文件 电动车所用的非隔离DC/DC电源&#xff0c;采用BUCK电路&#xff0c;运行稳定&#xff0c;为已经在产品中使用的电路 1、原理图 2、PCB

2006年下半年软件设计师【上午题】试题及答案

文章目录 2006年下半年软件设计师上午题--试题2006年下半年软件设计师上午题--答案2006年下半年软件设计师上午题–试题

linux rocky9.2系统搭建sqle数据库审核平台

文章目录 前言一、环境准备?二、开始部署前言 关于SQLE SQLE 是由上海爱可生信息技术股份有限公司 开发并开源,支持SQL审核、索引优化、事前审核、事后审核、支持标准化上线流程、原生支持 MySQL 审核且数据库类型可扩展的 SQL 审核工具。 产品特色 支持通过插件的形式扩展…

使用 WebGL 创建 3D 对象

WebGL Demohttps://mdn.github.io/dom-examples/webgl-examples/tutorial/sample5/index.html 现在让我们给之前的正方形添加五个面从而可以创建一个三维的立方体。最简单的方式就是通过调用方法 gl.drawElements() 使用顶点数组列表来替换之前的通过方法gl.drawArrays() 直接…

docker 多网卡指定网卡出网

前言 宿主机中有多个网卡 ens160 192.168.4.23/20 内网通信用 ens192 10.31.116.128/24 出公网访问-1 ens193 10.31.116.128/24 出公网访问-2 现在需要不同容器中不同出网访问&#xff0c;举例 容器1 192.168.0.1/20 网段走宿主机 ens160网卡&#xff0c;否则全部走ens192 网…

从@Param注解开始,深入了解 MyBatis 参数映射的原理

系列文章目录 MyBatis缓存原理 Mybatis plugin 的使用及原理 MyBatisSpringboot 启动到SQL执行全流程 数据库操作不再困难&#xff0c;MyBatis动态Sql标签解析 Mybatis的CachingExecutor与二级缓存 使用MybatisPlus还是MyBaits &#xff0c;开发者应该如何选择&#xff1f; 巧…