【JVM】类加载器ClassLoader

news2024/9/24 23:21:46

图片

一、简介

在Java中,类加载器(ClassLoader)是一个关键的组件,它负责将字节码文件加载到内存并转换成Java类。Java的类加载器主要可以分成两类:系统提供的和由Java应用开发人员编写的。Java开发者可以根据需要创建自己的类加载器。所有的类加载器都继承自抽象类ClassLoader。当JVM需要加载一个类时,它会首先请求父类加载器去尝试加载这个类,如果父类加载器无法找到相应的类或者该类的字节码文件,那么该请求就会传递给子类加载器,依此类推,直到某个类加载器找到了相应的字节码文件为止。

总的来说,Java中的类加载器是一个重要的环节,它确保了Java程序能够正确地运行和使用各种不同类型的类。

二、加载器分类

从JVM的角度看,类加载器可以分为两种:

  • 引导类加载器(启动类加载器 Bootstrap ClassLoader);

  • 其他所有类加载器,这些类加载器由 java 语言实现,独立存在于虚拟机外部,并且全部继承自抽象类 java.lang.ClassLoader。

- 引导类加载器
  - 扩展类加载器(Extension ClassLoader)
    - 应用程序类加载器(Application ClassLoader)
      - 自定义类加载器

从 java 开发人员的角度来看,类加载器就应当划分得更细致一些,自 JDK1.2 以来 java 一直保持者三层类加载器:

1. 引导类加载器(启动类加载器 BootStrap ClassLoader)

  • 这个类加载器使用 C/C++语言实现,嵌套在 JVM 内部.它用来加载 java 核心类库.并不继承于 java.lang.ClassLoader 没有父加载器。

  • 负责加载扩展类加载器和应用类加载器,并为他们指定父类加载器。

  • 出于安全考虑,引用类加载器只加载存放在<JAVA_HOME>\lib 目录,或者被-Xbootclasspath 参数锁指定的路径中存储放的类。

2. 扩展类加载器(Extension ClassLoader)

  • Java 语言编写的,由 sun.misc.Launcher$ExtClassLoader 实现;

  • 派生于 ClassLoader 类;

  • 从 java.ext.dirs 系统属性所指定的目录中加载类库,或从 JDK 系统安装目录的jre/lib/ext 子目录(扩展目录)下加载类库.如果用户创建的 jar 放在此目录下,也会自动由扩展类加载器加载。

3. 应用程序类加载器(系统类加载器 Application ClassLoader)

  • Java 语言编写的,由 sun.misc.Launcher$AppClassLoader 实现;

  • 派生于 ClassLoader 类;

  • 加载我们自己定义的类,用于加载用户类路径(classpath)上所有的类;

  • 该类加载器是程序中默认的类加载器;

  • ClassLoader 类 , 它 是 一 个 抽 象 类 , 其 后 所 有 的 类 加 载 器 都 继 承 自 ClassLoader(不包括启动类加载器)。

图片

自定义类加载器的实现通常继承自java.lang.ClassLoader类或其子类。以下是一个简单的自定义类加载器示例:



import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class CustomClassLoader extends ClassLoader {
    @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 fileName = getFileName(className);
        try (InputStream is = getParent().getResourceAsStream(fileName)) {
            if (is == null) {
                return null;
            }
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int bufferSize = 4096;
            byte[] buffer = new byte[bufferSize];
            int bytesNumRead;
            while ((bytesNumRead = is.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesNumRead);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

在这个示例中,我们创建了一个名为CustomClassLoader的自定义类加载器,它继承自ClassLoader类。我们重写了findClass方法,该方法接收一个字符串参数name,表示要加载的类的全名。在这个方法中,我们首先调用loadClassData方法来读取类的字节码数据,然后使用defineClass方法将字节码数据转换为Java类。

三、类加载过程

图片

1. 加载

  • 通过类名(地址)获取此类的二进制字节流。

  • 将这个字节流所代表的静态存储结构转换为方法区(元空间)的运行时结构。

  • 在内存中生成一个代表这个类的java.lang.class对象,作为这个类的各种数据的访问入口。

2. 链接

  • 验证:检验被加载的类是否有正确的内部结构,并和其他类协调一致;

    验证文件格式是否一致: class 文件在文件开头有特定的文件标识(字节码文件都以 CA FE BA BE 标识开头);主,次版本号是否在当前 java 虚拟机接收范围内;

    元数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合java 语言规范的要求,例如这个类是否有父类;是否继承浏览不允许被继承的类(final 修饰的类).....

  • 准备:准备阶段则负责为类的静态属性分配内存,并设置默认初始值;

    不包含用 final 修饰的 static 常量,在编译时进行初始化;

    例如: public static int value = 123;value 在准备阶段后的初始值是 0,而不是 123;

  • 解析:将类的二进制数据中的符号引用替换成直接引用(符号引用是 Class 文件的逻辑符号,直接引用指向的方法区中某一个地址)。

3. 初始化

初始化,为类的静态变量赋予正确的初始值,JVM 负责对类进行初始化,主要对类变量进行初始化。初始化阶段就是执行底层类构造器方法<clinit>()的过程。此方法不需要定义,是 javac 编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来的。

类什么时候初始化:

JVM规定:每个类或者接口被首次主动使用时才对其进行初始化。

  • 通过 new 关键字创建对象;

  • 访问类的静态变量,包括读取和更新;

  • 访问类的静态方法;

  • 对某个类进行反射操作;

  • 初始化子类会导致父类的的初始化;

  • 执行该类的 main 函数;

除了以上几种主动使用,以下情况被动使用,不会加载类:

  • 引用该类的静态常量,注意是常量,不会导致初始化,但是也有意外,这里的常量是指已经指定字面量的常量,对于那些需要一些计算才能得出结果的常量就会导致类加载,比如:

    public final static int NUMBER = 5 ; //不会导致类初始化,被动使用

    public final static int RANDOM = new Random().nextInt() ; //会导致类加载。

  • 构造某个类的数组时不会导致该类的初始化,比如:

    Student[] students = new Student[10] ;

类的初始化顺序

  • 对static修饰的变量或语句块进行赋值

    如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行;

    如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类;

    顺序是:父类 static –> 子类 static。

四、双亲委派机制

Java 虚拟机对 class 文件采用的是按需加载的方式,也就是说当需要该类时才会将它的 class 文件加载到内存中生成 class 对象。而且加载某个类的 class 文件时,Java 虚拟机采用的是双亲委派模式,即把请求交由父类处理,它是一种任务委派模式。

图片

工作原理:

  • 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请 求委托给父类的加载器去执行;

  • 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器。

  • 如果父类加载器可以完成类的加载任务,就成功返回,倘若父类加载器无法完成加载任务,子加载器才会尝试自己去加载,这就是双亲委派机制.

  • 如果均加载失败,就会抛出 ClassNotFoundException 异常。

双亲委派优点:

  • 安全,可避免用户自己编写的类替换 Java 的核心类,如 java.lang.String;

  • 避免类重复加载,当父亲已经加载了该类时,就没有必要子 ClassLoader 再加载一次。

如何打破双亲委派机制

Java 虚拟机的类加载器本身可以满足加载的要求,但是也允许开发者自定义类加载器。

在 ClassLoader 类中涉及类加载的方法有两个,loadClass(String name), findClass(String name),这两个方法并没有被 final 修饰,也就表示其他子类可以重写。

  • 重写 loadClass 方法(是实现双亲委派逻辑的地方,修改他会破坏双亲委派机制, 不推荐)。

  • 重写 findClass 方法 (推荐)。

我们可以通过自定义类加载重写方法打破双亲委派机制, 再例如 tomcat 等都有自己定义的类加载器。


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

    private byte[] loadClassData(String className) {
        // 这里可以根据实际情况从文件、网络等地方读取字节码数据
        // 为了简化示例,我们直接返回null
        return null;
    }
}

在这个示例中,MyClassLoader继承自ClassLoader,并重写了findClass方法。当JVM需要加载一个类时,它会首先请求父类加载器去尝试加载这个类,如果父类加载器无法找到相应的类或者该类的字节码文件,那么该请求就会传递给MyClassLoader,然后调用MyClassLoader的findClass方法。在findClass方法中,我们首先调用loadClassData方法来获取类的字节码数据,然后将这些字节码数据转换成Java类。

图片

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

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

相关文章

ES集群分片数据的高可用

文章目录 ES集群分片数据的高可用1. 集群设置索引节点2. 集群新增文档数据3.查看集群中文档数据分片节点4. 让节点9201宕机&#xff0c;查看其分片变化5. 让节点9201&#xff0c;查看分片变化 ES集群分片数据的高可用 集群中的索引主分片和副分片在不同的计算机上&#xff0c;如…

揭开 JavaScript 作用域的神秘面纱(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

pyqtgraph 教程

pyqtgraph 教程 简介 PyQtGraph 是一个用于科学和工程数据可视化的开源库&#xff0c;基于 PyQt 和 NumPy 构建而成。它提供了丰富的绘图工具和交互功能&#xff0c;可以用于创建高性能的实时数据图表、图像显示和信号处理应用。 以下是 PyQtGraph 的一些特点和功能&#xf…

.NET Standard 支持的 .NET Framework 和 .NET Core

.NET Standard 是针对多个 .NET 实现推出的一套正式的 .NET API 规范。 推出 .NET Standard 的背后动机是要提高 .NET 生态系统中的一致性。 .NET 5 及更高版本采用不同的方法来建立一致性&#xff0c;这种方法在大多数情况下都不需要 .NET Standard。 但如果要在 .NET Framewo…

基于sumo实现交通灯控制算法的模板

基于sumo实现交通灯控制算法的模板 目录 在windows安装run hello world networkroutesviewsettings & configurationsimulation 交通灯控制系统 介绍文件生成器类&#xff08;FileGenerator&#xff09;道路网络&#xff08;Network&#xff09;辅助函数生成道路网络&am…

从细菌基因组中提取噬菌体变异序列工具PhaseFinder的介绍、安装和使用方法

PhaseFinder ## 概览&#xff0c;不翻译了&#xff0c;大家自己看吧 The PhaseFinder algorithm is designed to detect DNA inversion mediated phase variation in bacterial genomes using genomic or metagenomic sequencing data. It works by identifying regions flank…

Java学习笔记(四)——正则表达式

文章目录 正则表达式基本规则字符类(只匹配一个字符)预定义字符(只匹配一个字符)数量词练习正则表达式插件 爬虫利用正则表达式获取想要的内容爬取网络信息练习有条件的爬取贪婪爬取非贪婪爬取正则表达式在字符串中的使用 分组捕获分组正则表达式外部使用非捕获分组正则表达式忽…

公共用例库计划--个人版(二)主体界面设计

1、任务概述 计划内容&#xff1a;完成公共用例库的开发实施工作&#xff0c;包括需求分析、系统设计、开发、测试、打包、运行维护等工作。 1.1、 已完成&#xff1a; 需求分析、数据库表的设计&#xff1a;公共用例库计划–个人版&#xff08;一&#xff09; 1.2、 本次待…

神经网络-卷积层

卷积 输入通道数&#xff0c; 输出通道数&#xff0c;核大小 参数具体含义 直观理解各个参数的网站(gif) https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md大概长这样&#xff0c;cyan是青色的意思 channel数&#xff08;终于理解论文里图片放好多层的原因…

VMware ESXI 8 安装ipmitool 调整戴尔服务器风扇转速

本文内容适合ESXI 8版本安装ipmitool &#xff0c;进行管理&#xff0c;已知的是8.0以上版本无法安装社区的vib.所以需要自己编译文件&#xff0c;7.0及之前的版本可以安装vib版本的ipmtools。 一、编译好的适用于esxi8的ipmitool下载 ipmitool下载 二、安装ipmitool 1、开…

铁塔基站数字化管理监测解决方案

截至2023年10月&#xff0c;我国5G基站总数达321.5万个&#xff0c;占全国通信基站总数的28.1%。然而&#xff0c;随着5G基站数量的快速增长&#xff0c;基站的能耗问题也逐渐日益凸显&#xff0c;基站的用电给运营商带来了巨大的电费开支压力&#xff0c;降低5G基站的能耗成为…

new FormData 同时发送表单 json 以及文件二进制流

需要新增时同时发送表单 json 以及对应的文件即可使用以下方法传参 let formDataParams new FormData(); 首先通过 new FormData&#xff08;&#xff09; 创建你需要最后发送的表单 接着将你的对象 json 存储&#xff0c;注意使用 new Blob 创建大表单转换成 json 格式。以…

根据MySql的表名,自动生成实体类,模仿ORM框架

ORM框架可以根据数据库的表自动生成实体类&#xff0c;以及相应CRUD操作 本文是一个自动生成实体类的工具&#xff0c;用于生成Mysql表对应的实体类。 新建Winform窗体应用程序AutoGenerateForm&#xff0c;框架(.net framework 4.5)&#xff0c; 添加对System.Configuration的…

了解nginx

1&#xff0c;概念 nginx是一个轻量级、高性能的HTTP和反向代理web服务器&#xff0c;同时也是一个通用代理服务器&#xff08;TCP、UDP、IMAP、POP3、SMTP&#xff09;。 2&#xff0c;优势 轻量级&#xff0c;占用内存少&#xff0c;启动极快采用事件驱动的异步非阻塞处理方…

linux中的系统安全

一.账号安全 将非登录用户的shell设为/sbin/nologin 系统中用户有三种&#xff1a;超级管理员 普通用户 程序用户 前两种用户可以登录系统&#xff0c;程序用户不给登录 所以称为非登录用户 命令格式&#xff1a; usermod -s /sbin/nologin&#xff08;改已有用户&#…

亲测表白网制作源码,在线制作表白,无数据库上传就能用

在线制作表白网源码 没有数据库上传就能用 后台/admin 账号密码都是admin

【mars3d】批量关闭矢量数据的startFlicker()闪烁或者全部关闭startFlicker()

问题 1.graphic/entity/billboard怎么能够批量关闭startFlicker()闪烁或者 全部关闭startFlicker()呢&#xff1f; 相关链接 1.http://mars3d.cn/editor-vue.html?idgraphic/entity/billboard 2.http://mars3d.cn/apidoc.html#FlickerEntity 期望效果 1.graphic.stopFlic…

Java:爬虫htmlunit

为什么htmlunit与HttpClient两者都可以爬虫、网页采集、通过网页自动写入数据&#xff0c;我们会推荐使用htmlunit呢? 一、网页的模拟化 首先说说HtmlUnit相对于HttpClient的最明显的一个好处&#xff0c;HtmlUnit更好的将一个网页封装成了一个对象&#xff0c;如果你非要说H…

数字后端设计实现 | 数字后端PR工具Innovus中如何创建不同高度的row?

吾爱IC社区星球学员问题&#xff1a;Innovus后端实现时两种种不同高度的site能做在一个pr里面吗&#xff1f; 答案是可以的。 Innovus支持在同一个设计中中使用不同的row&#xff0c;但需要给各自子模块创建power domain。这里所说的不同高度的row&#xff0c;有两种情况。 1…

数据库高可用mha

MHA搭建的步骤 一.配置主从复制 1.初始化环境 #在四台服务器上初始化环境 systemctl stop firewalld systemctl disable firewalld setenforce 0 2.修改 Master、Slave1、Slave2 节点的主机名 #在Master上 hostnamectl set-hostname mysql1 su#在Slave1 hostnamectl set-h…