深入分析ClassLocader工作机制

news2024/11/14 21:19:18

文章目录

    • 一、ClassLoader简介
      • 1. 概念
      • 2. ClassLoader类结构分析
    • 二、ClassLoader的双亲委派机制
    • 三、Class文件的加载流程
      • 1. 简介
      • 2. 加载字节码到内存
      • 3. 验证与解析
      • 4. 初始化Class对象
    • 四、常见加载类错误分析
      • 1. ClassNotFoundException
      • 2. NoClassDefFoundError
      • 3. UnsatisfiledLinkError
      • 4. ClassCastException
      • 5. ExceptionInInitializerError
    • 五、自定义ClassLoader的优势

一、ClassLoader简介

1. 概念

ClassLoader顾名思义就是类加载器,负责将Class加载到JVM中。事实上,ClassLoader除了能够将Class加载到JVM意外以外,还有一个重要的作用就是审查每个类应该由谁加载,它是一种父优先的等级加载机制。此外,ClassLoader除了上述的两个作用外还有一个任务就是将Class字节码重新解析成JVM统一要求的对象格式。

2. ClassLoader类结构分析

我们用到ClassLoader时常用下面的几个方法,以及它们的重载方法:

public abstract class ClassLoader {
	ClassLoader;
	Class<?> defineClass(byte[],int,int);
	Class<?> findClass(String);
	Class<?> loadClass(String);
	void resolveClass(Class<?>);
}

defineClass方法用来将byte字节流解析成JVM能够识别的Class对象,有了这个方法我们不仅仅可以通过class文件实例化对象,还可以通过其他方式如我们通过网络接收一个类的字节码,拿这个字节码流直接创建类的Class对象形式实例化对象。defineClass通常是和findClass方法一起使用的,我们通过直接覆盖ClassLoader父类的findClass方法来实现类的加载机制,从而取得想要加载类的字节码。然后调用defineClass方法生成类的Class对象,如果你想在类被加载到JVM中时就被链接,那么可以调用另一个resolveClass方法,当然你也可以选择让JVM来解决什么时候才链接到这个类。

如果你不想重新定义加载类的规则,只想在运行时能够加载自己指定的一个类而已,那么你可以用this.getClass().getClassLoader().loadClass("class")调用ClassLoader的loadclass方法可以获取这个类的Class对象,这个loadClass还有重载方法,你统一可以决定在上面时候解析这个类。

二、ClassLoader的双亲委派机制

双亲委派机制(Parent Delegation Mechanism)是Java中的一种类加载机制。在Java中,类加载器负责加载类的字节码并创建对应的Class对象。双亲委派机制是指当一个类加载器收到类加载请求时,它会先将该请求委派给它的父类加载器去尝试加载。只有当父类加载器无法加载该类时,子类加载器才会尝试加载。

这种机制的设计目的是为了保证类的加载是有序的,避免重复加载同一个类。Java中的类加载器形成了一个层次结构,根加载器(Bootstrap ClassLoader)位于最顶层,它负责加载Java核心类库。其他加载器如扩展类加载器(Extension ClassLoader)和应用程序类加载器(Application ClassLoader)都有各自的加载范围和职责。通过双亲委派机制,可以确保类在被加载时,先从上层的加载器开始查找,逐级向下,直到找到所需的类或者无法找到为止。

这种机制的好处是可以避免类的重复加载,提高了类加载的效率和安全性。同时,它也为Java提供了一种扩展机制,允许开发人员自定义类加载器,实现特定的加载策略。
在这里插入图片描述
其实Bootstrap ClassLoader并不属于JVM的类等级层次,因为BootStrap ClassLoader并没有遵守ClassLoader的加载规则,另外它并没有子类,ExtClassLoader的父类也不是Bootstrap ClassLoader,我们应用中能取到的顶层父类时ExtClassLoader。

ExtClassLoader和AppClassLoader都位于sun.misc.Launcher类中,它们是Loucher类的内部类。ExtClassLoader和AppClassLoader都继承了URLClassLoader,而URLClassLoader又实现了抽象类ClassLoader,在创建Launcher对象时会首先创建ExtClassLoader,然后将ExtClassLoader作为父加载器创建AppClassLoader对象,而通过Launcher.getClassLoade()方法获取的ClassLoader就是AppClassLoader对象。所以如果Java应用中没有定义其他ClassLoader,那么除了System.getProperty("java.ext.dirs")目录下的类是由ExtClassLoader加载为,其它类都由AppClassLoader来加载。

JVM加载class文件到内存中有两种方式:

  • 隐式加载:所谓隐式加载是不通过在代码里面调用ClassLoader来加载所需要的类,而是铜鼓oJVM来自动加载所需的类到内存的方式。例如:当我们在类中继承或者引用某个类是,JVM在解析当前这个类时发现引用不在内存中,那么自动将这些类加载到内存中。
  • 显式加载:相反的显式加载就是我们在代码中使用ClassLoader类加载一个类的方式

其实这两种方式是混合使用的,例如我们通过自定义的ClassLoader显式加载一个类时,这个类又引用了其他类,那么这些类就是隐式加载的。

三、Class文件的加载流程

1. 简介

下面分析如何将class文件加载到JVM中。ClassLoader加载一个class文件到JVM要经历如下阶段:

在这里插入图片描述

  • 首先找到class文件并把这个文件包含的字节码加载到内存中
  • 链接阶段分为三个步骤,分别是字节码验证、Class类数据结构分析及相应的内存分配和最后符号表的链接
  • 最后是类中静态数据和初始化赋值,以及静态块的执行

2. 加载字节码到内存

findClass()的方法是在ClassLoader实现类中实现的,例如URLClassLoader就实现了该方法,URLClassLoader类通过一个URLClassPath类的帮助取得要加载的class文件字节流,而这个URLClassPath定义了到哪里去找这个class文件,如果找到了这个class文件,再读取它的byte字节流通过调用defineClass()方法创建类对象。

 private final URLClassPath ucp;

再看其构造函数,要指定一个URL数据才能创建URLClassLoader对象,也就是必须要指定这个ClassLoader默认到哪个目录中去查找class文件

  public URLClassLoader(URL[] urls, ClassLoader parent) {
        super(parent);
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkCreateClassLoader();
        }
        this.acc = AccessController.getContext();
        ucp = new URLClassPath(urls, acc);
    }

在创建URLClassLoader对象时就根据传过来的URL数组中的路径来判断是文件还是jar包,根据路径不同分别创建FileLoader或者JarLoader,或者使用默认的加载器,当JVM调用findClass时由这几个加载器来将class文件加载到内存中。

3. 验证与解析

  • 字节码验证,类装入器对于类的字节码要做许多检测,以确保格式正确、行为争取
  • 类准备,这个阶段准备代表的每个类中定义的字段、方法和实现接口所必需的数据结构
  • 解析,在这个阶段类装入器装入类所引用的其他类。可以用许多方式引用类,如超类、结构、字段、方法签名、方法中使用的本地变量

4. 初始化Class对象

类中包含的静态初始化器都被执行,在这一阶段末尾静态字段被初始化默认值。

四、常见加载类错误分析

在执行Java程序时经常会碰到ClassNotFoundExceptionNoClassDefFoundError两个异常,它们都与类加载有关,下面分析一下产生这些异常的原因:

1. ClassNotFoundException

这个异常通常发生在显示加载类的时候,例如,用如下方式调用加载一个类时就报了这个错:

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName("Jack");
    }
}

在这里插入图片描述
出现这个错误的原因是,JVM要加载指定的文件的字节码到内存时,并没有找到这个文件对应的字节码,也就是这个文件并不存在(在当前classpath目录下)。

获取classpath路径的方法:this.getClass().getClassLoader().getResource("").toString()

2. NoClassDefFoundError

这个异常在第一次使用命令执行Java类时很可能会碰到,出现这种异常的可能原因是使用new关键字、属性引用某个类、继承了某个接口或类,以及方法的某个参数引用类某个类,这时会触发JVM的隐时加载这些类时发现这些类不存在。解决这个错误的方法就是确保每个类的引用的类都在当前的classpath下面。

3. UnsatisfiledLinkError

这个异常通常是JVM启动时,如果一不小心将JVM中的某个lib删除了,就可能会报这个错误。

public class Main {
    public native void nativeMethod();
    static {
        System.loadLibrary("Nolib");

    }
    public static void main(String[] args) throws ClassNotFoundException {
        new Main().nativeMethod();

    }
}

在这里插入图片描述
上面就是在解析native标识的方法时JVM找不到对应的本机库文件出现。

4. ClassCastException

这个错误比较常见,通常在程序中出现强制类型转换时出现这个错误。JVM在做类型转换时会按照如下规则进行检查:

  • 对于普通对象,对象必须是目标类的实例或目标类的子类实例。如果目标类是一个接口,那么会把它当作实现该接口的一个字类。
  • 对于数组类型,目标类必须是数组类型或java.lang.Objecgt、java.lang.CLoneable、java.io.Serializable

如果不满足上面规则,JVM就会报这个错误。要避免这个错误有两种方式:

  • 在容器类型中显示地指明这个容器所包含的对象类型
  • 先通过instanceof检查是不是目标类型,然后再进行强制类型转换

5. ExceptionInInitializerError

这个错误JVM规范中是这样定义的:

  • 如果Java虚拟机试图创建类ExceptionInInitializerError的新实例,但是因为出现Out-Of-Memory-Error而无法创建新实例,那么就会抛出OutOfMemoryError对象作为代替
  • 如果初始化器抛出一些Exception,而且Exception类不是Error或者它的某个子类,那么就会创建ExceptionInInitializerError类的一个新实例,并用Exception作为参数,用这个实例代替Exception

认值。

五、自定义ClassLoader的优势

通过前面的分析,ClassLoader能够完成的事情无非以下几种情况:

  • 在自定义路径下查找自定义的class文件,也许我们需要的class文件并不总是在已经设置好的ClassPath下面,那么我们必须想办法找到这个类,在这种情况下我们需要自己实现一个ClassLoader
  • 对我们自己的要加载的类做特殊处理,如保证通过网络传输的类的安全性,可以将类经过加密后再传输,在加载到JVM之前需要对类的字节码再解密,这个过程就可以在自定义的ClassLoader中实现
  • 可以定义类的实效机制,如果我们可以检查已经加载的class文件是否修改,如果修改类可以重新加载这个类,从而实现类的热部署。

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

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

相关文章

2023.12.13 关于 MySQL 复杂查询

目录 聚合查询 聚合函数 group by 子句 执行流程图 联合查询 笛卡尔积 内连接 外连接 左外连接 右外连接 自连接 子查询 单行子查询 多行子查询 EXISTS 关键字 合并查询 union on 和 union 的区别 聚合查询 聚合函数 函数说明COUNT([DISTINCT] expr)返回查询到…

wpf TelerikUI使用DragDropManager

首先&#xff0c;我先创建事务对象ApplicationInfo&#xff0c;当暴露出一对属性当例子集合对于构成ListBoxes。这个类在例子中显示如下代码&#xff1a; public class ApplicationInfo { public Double Price { get; set; } public String IconPath { get; set; } public …

【【ZYNQ 7020显示 图片 实验 】】

ZYNQ 7020显示 图片 实验 关键配置 BRAM 因为本次 我想显示的 图片是 400*400 所以在 内部 的 ROM 存储单元选择 了160000 ZYNQ7020的内部资源 最多是 大概 200000左右的 大小 大家可以根据 资源选择合适的像素 此处存放 内部的 图片转文字的COE文件 PLL设置 我选用的是按…

详解wmvcore.dll丢失的解决方法

wmvcore.dll是一款由Microsoft开发的Windows系统文件&#xff0c;主要用于存储和处理多媒体文件&#xff0c;尤其是Windows媒体视频。该文件对于音频和视频的播放至关重要。如果电脑上缺少这个文件&#xff0c;可能会出现播放问题或者相关的应用程序运行错误。在本文中&#xf…

Netty详解

目录标题 1、前期知识科普1.1 NIO 基本概念1.2 java BIO与NIO对比1.3 Reactor 模型 2、Netty 基础概念2.1 Netty 简介2.2 Netty 执行流程2.3 Netty 核心组件 3、Netty Demo编写3.1 总体框架3.2 具体代码 4、交流群 1、前期知识科普 1.1 NIO 基本概念 阻塞&#xff08;Block&a…

0x21 树与图的遍历

0x21 树与图的遍历 树与图最常见的储存方式就是使用一个邻接表保存它们的边集。邻接表以head数组为表头&#xff0c;使用ver和edge数组分别存储边的终点和权值&#xff0c;使用next数组模拟链表指针&#xff08;就像我们在0x13节中讲解邻接表所给出的代码那样&#xff09;。 …

科技铸就企业转型钢筋铁骨,群硕获评2023年度数字化影响力企业

12月15日&#xff0c;STIF2023第四届国际科创节暨DSC2023国际数字服务大会在北京顺利举行&#xff0c;本次大会以“数实融合 推动高质量发展”为主题&#xff0c;各大科技服务企业齐聚一堂&#xff0c;共同探讨2023科技发展新趋势。 大会上&#xff0c;群硕软件继2022年后再度…

Java版商城:Spring Cloud+SpringBoot b2b2c实现多商家入驻、直播带货及免 费小程序商城搭建

1. 涉及平台 平台管理、商家端&#xff08;pc端、手机端&#xff09;、买家平台&#xff08;h5/公众号、小程序、app端&#xff08;ios/android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 spring cloud、spring boot、mybatis、redis 3. 前端框架…

如何远程访问Axure RP制作的本地web站点实现协同办公

文章目录 前言1.在AxureRP中生成HTML文件2.配置IIS服务3.添加防火墙安全策略4.使用cpolar内网穿透实现公网访问4.1 登录cpolar web ui管理界面4.2 启动website隧道4.3 获取公网URL地址4.4. 公网远程访问内网web站点4.5 配置固定二级子域名公网访问内网web站点4.5.1创建一条固定…

浅析AI视频分析与视频管理系统EasyCVR平台及场景应用

人工智能的战略重要性导致对视频智能分析的需求不断增加。鉴于人工智能视觉技术的巨大潜力&#xff0c;人们的注意力正在从传统的视频监控转移到计算机视觉的监控过程自动化。 1、什么是视频分析&#xff1f; 视频分析或视频识别技术&#xff0c;是指从视频片段中提取有用信息…

java.lang.UnsupportedOperationException

一、背景 记录一次小坑… 最近在写一个关于Excel导出的小需求&#xff0c;由于系统都有一些工具类&#xff0c;还有原来已经做好的导出&#xff0c;直接拿过来改了改就用了&#xff0c;没想到直接报错&#xff0c;尴尬。 还是那句话&#xff0c;别人都能用&#xff0c;我复制…

innovus:ccopt_design流程

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f; 拾陆楼知识星球入口 ccopt完整的流程包括如下几个步骤&#xff1a; spec文件可以只创建一次&#xff0c;无需多次创建。 1&#xff09;clustering阶段 set_ccopt_property balance_mode cluster …

产品经理之Axure的元件库使用详细案例

⭐⭐ 产品经理专栏&#xff1a;产品专栏 ⭐⭐ 个人主页&#xff1a;个人主页 ​ 目录 前言 一.Axure的元件库的使用 1.1 元件介绍 1.2 基本元件的使用 1.2.1 矩形、按钮、标题的使用 1.2.2 图片及热区的使用 1.3 表单元件及表格元件的使用 1.3.1表单元件的使用 1.3.…

NFS|在linux环境下的安装和配置NFS

简介 NFS全称网络文件系统&#xff0c;可用于不同服务器之间的文件共享。 接下来介绍下NFS在linux环境下安装和配置。主要分为服务端和客户端。 服务端安装 开启rpcbind/portmap和nfs服务 # service portmaper start [rootlocalhost java]# service portmap start Redirectin…

低代码平台浅析:引迈JNPF

低代码平台能够改变应用交付和管理的模式&#xff0c;大幅缩减交付周期&#xff0c;最终帮助业务加速创新。引迈JNPF作为当中的一个低代码平台&#xff0c;其在用户体系方面做得怎样呢&#xff1f;我针对引迈JNPF进行了相关体验与测评&#xff0c;一起来看下。 低代码平台体验简…

SpringBoot Starter机制 ——自动化配置

目录 一、Starter机制 1.1 什么是 SpringBoot Starter 1.2 SpringBoot Starter 的作用 1.3 Starter的应用场景 二、案例 2.1 模拟短信发送模版 2.2 AOP实现日志切面模版 一、Starter机制 1.1 什么是 SpringBoot Starter Spring Boot Starter是Spring Boot框架提供的一种…

[Linux] LVS负载均衡群集+NAT部署

一、LVS负载均衡群集知识 1.1 群集的的定义及意义 Cluster&#xff0c;集群&#xff08;也称群集&#xff09;由多台主机构成&#xff0c;但对外只表现为一一个整体&#xff0c;只提供一-个访问入口(域名或IP地址)&#xff0c; 相当于一台大型计算机。 群集的作用&#xff1…

upload-labs笔记

简介 upload-labs是一个使用php语言编写的&#xff0c;专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助大家对上传漏洞有一个全面的了解。目前一共21关&#xff0c;每一关都包含着不同上传方式。 文件上传漏洞是指&#xff1a; Web 服务器允许用户将文件上传至其…

使用blip2进行图片输入文本输出

多模态的重要模型blip2,官方提供模型可以直接用来图片生成文本 github地址&#xff1a;https://github.com/salesforce/LAVIS/tree/main/projects/blip2 个人相当于跑了一下blip2的demo&#xff0c;记录下过程&#xff0c;供今后需要参考&#xff1a; 1、首先是环境安装&#…

Spring上下文之注解模块ConfigurationMethod

博主介绍:✌全网粉丝5W+,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验✌ 博主作品:《Java项目案例》主要基于SpringBoot+MyBatis/MyBatis-plus+…