JVM之类加载器

文章目录

  • 版权声明
  • 类加载器
  • 类加载器的分类
    • 启动类加载器
    • 拓展类加载器&应用程序类加载器
  • 双亲委派机制
    • 解决三个问题
  • 打破双亲委派机制
    • 自定义类加载器
    • 案例演示
    • 线程上下文类加载器
    • 案例梳理
    • OSGi模块化

版权声明

  • 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明,所有版权属于黑马程序员或相关权利人所有。本博客的目的仅为个人学习和交流之用,并非商业用途。
  • 我在整理学习笔记的过程中尽力确保准确性,但无法保证内容的完整性和时效性。本博客的内容可能会随着时间的推移而过时或需要更新。
  • 若您是黑马程序员或相关权利人,如有任何侵犯版权的地方,请您及时联系我,我将立即予以删除或进行必要的修改。
  • 对于其他读者,请在阅读本博客内容时保持遵守相关法律法规和道德准则,谨慎参考,并自行承担因此产生的风险和责任。本博客中的部分观点和意见仅代表我个人,不代表黑马程序员的立场。

类加载器

  • 类加载器(ClassLoader)是Java虚拟机提供给应用程序去实现获取类和接口字节码数据的技术。
  • 类加载器只参与加载过程中的字节码获取并加载到内存这一部分
    在这里插入图片描述
    在这里插入图片描述
  • 类加载器的应用场景
    在这里插入图片描述

类加载器的分类

  • 类加载器分为两类,一类是Java代码中实现的,一类是Java虚拟机底层源码实现的。
    -
  • 类加载器的设计JDK8和8之后的版本差别较大,JDK8及之前的版本中默认的类加载器有如下几种
    在这里插入图片描述
  • 类加载器的详细信息可以通过classloader命令查看:
    • classloader - 查看 classloader 的继承树,urls,类加载信息,使用 classloader 去getResource
    • 详细的指令教程请参考Arthas官网
[arthas@17048]$ classloader
 name                                       numberOfInstances  loadedCountTotal
 BootstrapClassLoader                       1                  2248
 com.taobao.arthas.agent.ArthasClassloader  1                  1351
 sun.misc.Launcher$ExtClassLoader           1                  47
 sun.reflect.DelegatingClassLoader          15                 15
 sun.misc.Launcher$AppClassLoader           1                  8
Affect(row-cnt:5) cost in 7 ms.
[arthas@17048]$ classloader -t
+-BootstrapClassLoader
+-sun.misc.Launcher$ExtClassLoader@7fe21cf8
  +-com.taobao.arthas.agent.ArthasClassloader@53e59711
  +-sun.misc.Launcher$AppClassLoader@18b4aac2
Affect(row-cnt:4) cost in 5 ms.
  • BootstrapClassLoader:c++编写的启动类加载器,位于虚拟机的源码中
    • numberOfInstances :1 类加载器的个数为一个
    • loadedCountTotal: 2248 共加载了2248个核心类
  • ArthasClassloader: ArthasClassloader工具的加载器
  • ExtClassLoader:拓展类加载器
  • DelegatingClassLoader: jdk底层实现用来提升反射效率的加载器
  • AppClassLoader:应用程序类加载器

启动类加载器

  • 启动类加载器(Bootstrap ClassLoader)是由Hotspot虚拟机提供的、使用C++编写的类加载器
  • 默认加载Java安装目录/jre/lib下的类文件,比如rt.jar,tools.jar,resources.jar等。
    在这里插入图片描述
  • 通过启动类加载器去加载用户jar包
    • 放入jre/lib下进行扩展:不推荐,尽可能不要去更改JDK安装目录中的内容,会出现即时放进去由于文件名不匹配的问题也不会正常地被加载
    • 使用参数进行扩展:推荐,使用-Xbootclasspath/a:jar包目录/jar包名 进行扩展

拓展类加载器&应用程序类加载器

  • 扩展类加载器和应用程序类加载器都是JDK中提供的、使用Java编写的类加载器

  • 它们的源码都位于sun.misc.Launcher中,是一个静态内部类,继承自URLClassLoader。通过目录或者指定jar包将字节码文件加载到内存中。
    在这里插入图片描述

  • 扩展类加载器(Extension Class Loader)是JDK中提供的、使用Java编写的类加载器

  • 默认加载Java安装目录/jre/lib/ext下的类文件

在这里插入图片描述

  • 通过扩展类加载器去加载用户jar包
    • 放入/jre/lib/ext下进行扩展:不推荐,尽可能不要去更改JDK安装目录中的内容
  • 使用参数进行扩展
    • 推荐,使用-Djava.ext.dirs=jar包目录 进行扩展,这种方式会覆盖掉原始目录,可以用;(windows):(macos/linux)追加上原始目录
  • 类加载器的加载路径可以先使用classloader -l查看类的hash值,然后通过classloader –c hash值 查看
    在这里插入图片描述

双亲委派机制

  • 由于Java虚拟机中有多个类加载器,那么一个类到底由哪个加载器进行加载?
  • 解决方案:双亲委派机制 双亲委派机制的核心是解决一个类到底由谁加载的问题
    在这里插入图片描述
  • 每个Java实现的类加载器中保存了一个成员变量叫“父”(Parent)类加载器,可以理解为它的上级,并不是继承关系。
    在这里插入图片描述
  • 启动类加载器使用C++编写,没有上级类加载器
  • 应用程序类加载器的parent父类加载器是扩展类加载器,而扩展类加载器的parent是空。扩展类加载器(Extension ClassLoader)在类加载过程中会委托给启动类加载器(Bootstrap ClassLoader)。这是因为Java的类加载器遵循所谓的"双亲委派模型"。
图1 真实的父类关系
图2 双亲委派机制中的逻辑关系
  • 尽管在类加载器的层次结构中,扩展类加载器的父类加载器是启动类加载器,但实际上,在类的加载过程中,应用类加载器的父类加载器是扩展类加载器,扩展类加载器没有父类加载器,但会委派给启动类加载器加载。这就是通常说扩展类加载器的父类加载器是启动类加载器。
  • 类加载器的继承关系可以通过classloader –t 查看
  • 双亲委派机制:当一个类加载器收到类加载请求时,它并不会尝试立即加载这个类,而是把这个请求委派给父类加载器。这样,每个类加载请求都会传递到启动类加载器,只有当父类加载器无法处理这个请求时(例如,类不在它的搜索路径中),子类加载器才会尝试自己去加载。

  • 双亲委派机制口诀:自底向上查找是否加载过,再由顶向下进行加载。
    在这里插入图片描述
    在这里插入图片描述

请添加图片描述
在这里插入图片描述

解决三个问题

  1. 重复的类:如果一个类重复出现在三个类加载器的加载位置,应该由谁来加载?
    • 启动类加载器加载,根据双亲委派机制,它的优先级是最高的
  2. String类覆盖问题:在自己的项目中去创建一个java.lang.String类,会被加载吗?
    • 不能,会交由启动类加载器加载在rt.jar包中的String类
  3. 类加载器的关系:这几个类加载器彼此之间存在关系吗?
    • 应用类加载器的父类加载器是扩展类加载器,扩展类加载器没有父类加载器,但是会委派给启动类加载器加载

打破双亲委派机制

  • 打破双亲委派机制的三种方式
  1. 自定义类加载器
    • 自定义类加载器并且重写loadClass方法,就可以将双亲委派机制的代码去除
  2. 线程上下文类加载器
    • Tomcat通过这种方式实现应用之间类隔离
  3. Osgi框架的类加载器
    • 历史上Osgi框架实现了一套新的类加载器机制,允许同级之间委托进行类的加载

自定义类加载器

  • 一个Tomcat程序中是可以运行多个Web应用的,如果这两个应用中出现了相同限定名的类,比如Servlet类,Tomcat要保证这两个类都能加载并且它们应该是不同的类。
  • 如果不打破双亲委派机制,当应用类加载器加载Web应用1中的MyServlet之后,Web应用2中相同限定名的MyServlet类就无法被加载。
    在这里插入图片描述
  • Tomcat使用了自定义类加载器来实现应用之间类的隔离。每一个应用会有一个独立的类加载器加载对应的类。
    在这里插入图片描述

  1. 先来分析ClassLoader的原理,ClassLoader中包含了4个核心方法
  2. 双亲委派机制的核心代码就位于loadClass方法中
    在这里插入图片描述
public Class<?> loadClass(String name) throws ClassNotFoundException {
    return this.loadClass(name, false); //resolve
}

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    //加锁 保证只有一个线程加载类
    synchronized(this.getClassLoadingLock(name)) {
        //查询当前类加载器是否加载过需要的类,true 返回类对象 否则,返回null
        Class<?> c = this.findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
			//向逻辑父类委派加载任务
            try {
                //直到委派到顶级父类——Extension,进入else分支
                if (this.parent != null) {
                    c = this.parent.loadClass(name, false);
                } else {
                    //调用启动类加载器,进行加载,无法加载返回null
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException var10) {
            }
			//所有类加载器加载失败,使用本类加载器进行加载
            if (c == null) {
                long t1 = System.nanoTime();
                c = this.findClass(name);
                PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                PerfCounter.getFindClasses().increment();
            }
        }

        if (resolve) {
            this.resolveClass(c);
        }

        return c;
    }
}

final Class<?> loadClass(Module module, String name) {
    synchronized(this.getClassLoadingLock(name)) {
        Class<?> c = this.findLoadedClass(name);
        if (c == null) {
            c = this.findClass(module.getName(), name);
        }

        return c != null && c.getModule() == module ? c : null;
    }
}
//读取二进制数据
protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

protected Class<?> findClass(String moduleName, String name) {
    if (moduleName == null) {
        try {
            return this.findClass(name);
        } catch (ClassNotFoundException var4) {
        }
    }

    return null;
}
//类名校验 将字节码信息加载到虚拟机内存中
protected final Class<?> defineClass(byte[] b, int off, int len) throws ClassFormatError {
    return this.defineClass((String)null, b, off, len, (ProtectionDomain)null);
}

protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError {
    return this.defineClass(name, b, off, len, (ProtectionDomain)null);
}

protected final void resolveClass(Class<?> c) {
    if (c == null) {
        throw new NullPointerException();
    }
}

  • 阅读双亲委派机制的核心代码,分析如何通过自定义的类加载器打破双亲委派机制
  • 打破双亲委派机制的核心就是将下边这一段代码重新实现
//parent等于null说明父类加载器是启动类加载器,直接调用findBootstrapClassOrNull
//否则调用父类加载器的加载方法
if (parent != null) {
	 c = parent.loadClass(name, false);
 } else {
	 c = findBootstrapClassOrNull(name);
 }
//父类加载器爱莫能助,我来加载!
if (c == null) 
	 c = findClass(name);

案例演示

public class CustomClassLoader extends ClassLoader {

    public CustomClassLoader(ClassLoader parent) {
        super(parent);
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        
        // 简单的例子, 只处理自定义的类
        if (name.startsWith("com.mydomain")) {
            return findClass(name);
        }

        // 其他的类还是采用双亲委派机制加载
        return super.loadClass(name);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            byte[] bytes = loadClassData(name);
            return defineClass(name, bytes, 0, bytes.length);
        } catch (IOException e) {
            throw new ClassNotFoundException("Cannot load class " + name, e);
        }
    }

    private byte[] loadClassData(String name) throws IOException {
        // 根据类名加载类的二进制数据. 假设所有类都在一个固定的目录下
        Path path = Paths.get("classes", name.replace('.', '/') + ".class");
        return Files.readAllBytes(path);
    }

}

  1. 自定义类加载器默认父类是AppClassLoader
  • 以Jdk8为例,ClassLoader类中提供了构造方法设置parent的内容
    在这里插入图片描述
  • 这个构造方法由另外一个构造方法调用,其中父类加载器由getSystemClassLoader方法设置,该方法返回的是AppClassLoader。
    在这里插入图片描述
  • 两个自定义类加载器加载相同限定名的类,不会冲突
    • 在同一个Java虚拟机中,只有相同类加载器+相同的类限定名才会被认为是同一个类

  • 正确的去实现一个自定义类加载器的方式是重写findClass方法,这样不会破坏双亲委派机制
    在这里插入图片描述

线程上下文类加载器

  • 利用上下文类加载器加载类,比如JDBC和JNDI等
  • JDBC中使用了DriverManager来管理项目中引入的不同数据库的驱动,比如mysql驱动、oracle驱动。
    在这里插入图片描述
  1. DriverManager类位于rt.jar包中,由启动类加载器加载
    在这里插入图片描述

  2. 依赖中的mysql驱动对应的类,由应用程序类加载器来加载。
    在这里插入图片描述

  • DriverManager属于rt.jar是启动类加载器加载的。而用户jar包中的驱动需要由应用类加载器加载,这就违反了双亲委派机制
    在这里插入图片描述
  • DriverManage使用SPI机制,最终加载jar包中对应的驱动类
    在这里插入图片描述
  • SPI中获取到应用程序类加载器
    • SPI中使用了线程上下文中保存的类加载器进行类的加载,这个类加载器一般是应用程序类加载器
      在这里插入图片描述

案例梳理

⚫1. 启动类加载器加载DriverManager。
⚫ 2. 在初始化DriverManager时,通过SPI机制加载jar包中的myql驱动。
⚫ 3. SPI中利用了线程上下文类加载器(应用程序类加载器)去加载类并创建对象。

  • 由启动类加载器加载的类,委派应用程序类加载器去加载类的方式,打破了双亲委派机制
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

OSGi模块化

  • OSGi模块化框架。它存在同级之间的类加载器的委托加载。OSGi还使用类加载器实现了热部署的功能。
  • 热部署指的是在服务不停止的情况下,动态地更新字节码文件到内存中
    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Angular 由一个bug说起之一:List / Grid的性能问题

在angular中&#xff0c;MatTable构建简单&#xff0c;使用范围广。但某些时候会出现卡顿 卡顿情景&#xff1a; 1&#xff1a;一次性请求太多的数据 2&#xff1a;一次性渲染太多数据&#xff0c;这会花费CPU很多时间 3&#xff1a;行内嵌套复杂的元素 4&#xff1a;使用过多的…

iOS应用加固方案解析:ipa加固安全技术全面评测

​ 在移动应用开发领域&#xff0c;iOS应用的安全性一直备受关注。ipaguard作为一款专业的iOS应用加固方案&#xff0c;采用混淆加密技术&#xff0c;旨在保护应用免受破解、逆向和篡改等风险。本文将深入探讨ipaguard的产品功能、安全技术及其在iOS应用加固领域中的核心优势和…

离线视频ocr识别

sudo apt-get install libleptonica-dev libtesseract-dev sudo apt-get install tesseract-ocr-chi-sim python -m pip install video-ocrwindows安装方法&#xff1a; 下载安装 https://digi.bib.uni-mannheim.de/tesseract/tesseract-ocr-w64-setup-5.3.3.20231005.exe 下…

Python 编码最全梳理

为什么要写这篇文章呢&#xff1f;这里就要提到某一天&#xff0c;工作的时候&#xff0c;突然发现自己在编码方面&#xff0c;一窍不通。实在惭愧 字符编码是计算机技术的基石&#xff0c;对于程序员来说尤其重要&#xff0c;字符编码的知识是必须要懂的 编码入门知识 ASCI…

Spring Boot 集成 ElasticSearch

1 加入依赖 首先创建一个项目&#xff0c;在项目中加入 ES 相关依赖&#xff0c;具体依赖如下所示&#xff1a; <dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.1.0</version&g…

leetcode 62

leetcode 62 题目 解题思路 class Solution { public:int uniquePaths(int m, int n) {vector<vector<int>> f(m, vector<int>(n));for(int i0; i<m; i){f[i][0] 1;}for(int j0; j<n; j){f[0][j] 1;}for(int i1; i<m; i){for(int j1; j<n; j){…

C语言:对于宏的一些概念及技巧

一、前言 宏在C语言中是一段有名称的代码段&#xff0c;在程序编译过程中&#xff0c;会将宏的内容被这段代码进行替换&#xff0c;常常用于定义一些常量、函数、代码块等&#xff0c;由于近年来发现许多公司进行面试时对于宏的面试题尤为多&#xff0c;故本文将对C语言中的宏的…

说说React render方法的原理?在什么时候会被触发?

一、原理 首先&#xff0c;render函数在react中有两种形式&#xff1a; 在类组件中&#xff0c;指的是render方法&#xff1a; class Foo extends React.Component { render() { return <h1> Foo </h1>; } } 在函数组件中&#xff0c;指的是函…

C语言—统计从键盘输入的一行英文句子的字符个数

流程图 代码 #include <stdio.h>int main() {int count0;printf("请输入英文字符&#xff0c;回车确认&#xff1a;");while (getchar()!\n){count count 1;}printf("共输入%d个字符\n", count);system("pause");return 0; }请输入英文字…

一文入门Springboot+actuator+Prometheus+Grafana

环境介绍 技术栈 springbootmybatis-plusmysqloracleactuatorPrometheusGrafana 软件 版本 mysql 8 IDEA IntelliJ IDEA 2022.2.1 JDK 1.8 Spring Boot 2.7.13 mybatis-plus 3.5.3.2 本地主机应用 192.168.1.9:8007 PrometheusGrafana安装在同一台主机 http://…

【有限元方法】Newton-Raphson Method

Newton-Raphson Method Linear vs Nonlinear Analysis: At this point, we can conduct a linear analysis no problem ∫ ∑ i , j 1 3 σ i j ε i j ∗ d v ∫ t n ⋅ u ∗ d s ∫ ρ b ⋅ u ∗ d v ⇒ ∫ e [ B ] T [ C ] [ B ] d x ⏟ k e u e ∫ ∂ e [ N ] T t n …

CSS3 分页、框大小、弹性盒子

一、CSS3分页&#xff1a; 网站有很多个页面&#xff0c;需要使用分页来为每个页面做导航。示例&#xff1a; <style> ul.pagination { display: inline-block; padding: 0; margin: 0; } ul.pagination li {display: inline;} ul.pagination li a { color: black; f…

[工业自动化-9]:西门子S7-15xxx编程 - PLC主站 - 信号量:模拟量

目录 前言&#xff1a; 一、模拟量模块 1.1 概述 1.2 安装 1.3 模拟量链接线 二、模拟量常见问题 2.1 两线制、四线制&#xff08;电流&#xff09; 2.2 模拟量模块的参数 2.3 差分信号与单端信号 三、如何防止电磁干扰 3.1 概述 3.2 工业现场的电磁干扰源来源 3.…

3D物理模拟和视觉特效软件SideFX Houdini mac中文介绍

SideFX Houdini for mac是一款3D物理模拟和视觉特效软件&#xff0c;几乎所有好莱坞特效电影里的物理模拟&#xff0c;包括碎裂&#xff0c;烟尘&#xff0c;碰撞&#xff0c;火焰&#xff0c;流体等模拟&#xff0c;都看得到它的身影。其独特的节点式操作方式&#xff0c;尤其…

Anaconda Powershell Prompt和Anaconda Prompt的区别

先说结论&#xff1a;主要功能应该一样。区别在于powershell支持的命令更多。比如查询路径的命令pwd和列表命令ls。 Anaconda PowerShell Prompt和Anaconda Prompt是Anaconda发行版中两个不同的命令提示符工具。 Anaconda Prompt是Anaconda发布的默认命令提示符工具&#xff0…

前端开发神器之 VsCode AI 辅助插件 DevChat

目录 前言DevChat介绍DevChat 独特优势注册账号安装插件设置密钥访问指令AI 解疑 最后 #AI编程助手哪家好&#xff1f;DevChat“真”好用 # 前言 我们都有过写代码时反复看了半天也不知道bug在哪&#xff0c;大大浪费了时间。一些基础的代码可能看一会儿能够解决&#xff0c;但…

DDoS攻击剧增,深入解析抗DDoS防护方案

当下DDoS攻击规模不断突破上限&#xff0c;攻击方式越发复杂。面对复杂的攻击形式&#xff0c;对于企业和组织来说无疑需要更完备的抗DDoS方案&#xff0c;依靠传统的解决方法并不能做到一劳永逸。在服务器抵抗DDoS防护上&#xff0c;你不会忽略F5的产品&#xff0c;让我们一起…

【Git】gui图形化界面的使用、ssh协议以及idea集成Git

目录 gui图形化界面的使用 介绍 特点 gui图形的使用 ssh协议 介绍 步骤及概念 ssh协议的使用 配置公钥 idea集成Git idea配置git IDEA安装gitee IDEA中登入Git ​编辑 项目分享 克隆分享的项目 ​编辑 ​编辑 idea上传远程 gui图形化界面的使用 介绍 GUI&#xff08…

4 Paimon数据湖之Hive Catalog的使用

更多Paimon数据湖内容请关注&#xff1a;https://edu.51cto.com/course/35051.html Paimon提供了两种类型的Catalog&#xff1a;Filesystem Catalog和Hive Catalog。 Filesystem Catalog&#xff1a;会把元数据信息存储到文件系统里面。Hive Catalog&#xff1a;则会把元数据…

什么是进程等待?

什么是进程等待 在了解进程等待之前&#xff0c;我们要回顾一下什么是僵尸进程&#xff1a;是指一个已经终止执行的进程&#xff0c;但其父进程还没有通过 wait() 系统调用来获取该进程的退出状态信息。当一个进程正常退出或者被终止时&#xff0c;其所占用的系统资源会被操作…
最新文章