Java类加载器详解

article/2025/9/18 7:11:28

1 特点

双亲委派:

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

负责依赖:

同时加载它依赖的类

缓存加载:

加载后放到内存里,再次加载时就会直接获取

2 应用

类层次划分,osgi,热部署,代码加密

3 分类

3.1 启动类加载器 Bootstrap Classloader

负责将存放在 JAVA_HOME/lib 目录的,或者被 -Xbootclasspath 参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中。

 BootstrapClassLoader 对 Java程序是不可见的,所以获取时返回了 null,我们也可以通过某一个类的加载器是否为 null 来作为判断该类是不是使用 BootstrapClassLoader 进行加载的依据

3.2 扩展类加载器 Extension ClassLoader

sun.misc.Launcher$ExtClassLoader ,负责加载 JAVA_HOME\lib\ext,java.ext.dirs 系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。

3.3 应用程序类加载器 Application ClassLoader

sun.misc.Launcher$AppClassLoader, 负责加载用户类路径上所指定的类库,可以直接使用这个类加载器。

3.4 系统类加载器system class loader

默认是应用程序类加载器

 系统类加载器配置:

使用如下配置,可以将自定义的类加载器配置成系统类加载器

-Djava.system.class.loader=me.capthua.advancedjava.week1.classloader.MyClassLoader

此时,自定义类必须实现如下的构造方法

public MyClassLoader(ClassLoader classLoader){super(classLoader);
}

3.5 自定义类加载器

示例代码如下:

package me.ffulauh.javalang;import java.io.IOException;
import java.io.InputStream;public class ClassLoaderTest {public static void main(String[] args)throws Exception {ClassLoader myLoader=new ClassLoader() {@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException {try {String fileName=name.substring(name.lastIndexOf(".")+1)+".class";InputStream is=getClass().getResourceAsStream(fileName);if(is==null){return super.loadClass(name);}byte[] b=new byte[is.available()];is.read(b);return defineClass(name,b,0,b.length);} catch (IOException e){throw new ClassNotFoundException(name);}}};Object obj=myLoader.loadClass("me.ffulauh.javalang.ClassLoaderTest").newInstance();System.out.println(obj.getClass());//class me.ffulauh.javalang.ClassLoaderTestSystem.out.println(obj.getClass().getClassLoader());//me.ffulauh.javalang.ClassLoaderTest$1@d716361System.out.println(me.ffulauh.javalang.ClassLoaderTest.class.getClassLoader());//sun.misc.Launcher$AppClassLoader@18b4aac2System.out.println(obj instanceof me.ffulauh.javalang.ClassLoaderTest);//false}
}

上面自定义的类加载器的逻辑是:从资源文件中读入一个字节码文件的输入流,如果流为空,则让父 类加载器加载,否则,由自定义的类加载器加载。

此逻辑为 子 类加载器 优先加载。不属于双亲委派机制。当然这里的逻辑有开发者自定定义。也可以显然 父 类加载器加载。

自定义类加载器的 父 类加载器是应用程序类加载器:

4 类加载器源码分析

4.1 ClassLoader

ClassLoader中的主要方法为:loadClass(String name, boolean resolve) 方法中 resolve 表示是否连接,默认为 false

先检查是否已经被加载过,若没有加载则调用父加载器的loadClass(),若父加载器为空则默认使用启动类加载器作为父加载器。如果父加载器加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。findClass()需要子类实现。

ClassLoader.java
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}
protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);
}

4.2 URLClassLoader

AppClassLoader,ExtClassLoader都继承URLClassLoader。

URLClassLoader 中的 findClass 调用的是 ClassLoader中的defineClass(),defineClass 中会调用 SecureClassLoader 中的 defineClass方法。

URLClassLoader.java
protected Class<?> findClass(final String name)throws ClassNotFoundException
{......return defineClass(name, res);......
}
private Class<?> defineClass(String name, Resource res) throws IOException {......return defineClass(name, b, 0, b.length, cs);......
}SecureClassLoader.java
protected final Class<?> defineClass(String name,byte[] b, int off, int len,CodeSource cs)
{return defineClass(name, b, off, len, getProtectionDomain(cs));
}

ClassLoader中的的 addURL(URL url) 

这个方法是将特定的 url 添加到类加载器,这些 url 是用来搜索类与资源的。将类注入现有累加载器是会用到这个方法。

/*** Appends the specified URL to the list of URLs to search for classes and resources.* If the URL specified is null or is already in the list of URLs, or if this loader is closed, then invoking this method has no effect.*/
protected void addURL(URL url) {ucp.addURL(url);
}

5.添加引用类的几种方式

1. 将类放到指定目录

放到 JDK 的 JAVA_HOME/lib/ext 下, -Djava.ext.dirs 指定目录, java -cp/classpath 指定类路径

2. 自定义 ClassLoader 加载

3. 将类注入已有的ClassLoader。拿到当前执行类的 ClassLoader,反射调用 defineClass 或者 addUrl方法 让已有的类加载器加载新类。

代码如下:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();String byteFilePath = "E:\\ideaProjects\\tech4u\\src\\main\\java\\me\\ffulauh\\javalang\\jvm\\classloader" +"\\HelloWorld.class";
File byteFile=new File(byteFilePath);
byte[] classBytes= FileUtils.file2byte(byteFile);
String className="me.ffulauh.javalang.jvm.classloader.HelloWorld";
Method defineMethod = ClassLoader.class.getDeclaredMethod("defineClass",String.class,byte[].class,int.class,int.class);
defineMethod.setAccessible(true);
Class helloClazz =(Class) defineMethod.invoke(classLoader,className, classBytes, 0, classBytes.length);
Method method=helloClazz.getMethod("sayHello");
method.invoke(helloClazz.newInstance());String jarFile="D:\\devtools\\repo\\com\\alibaba\\druid\\1.2.6\\druid-1.2.6.jar";
Class jarClazz=null;
Method addUrlMethod= URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addUrlMethod.setAccessible(true);
addUrlMethod.invoke(classLoader,new File(jarFile).toURI().toURL());
jarClazz=Class.forName("com.alibaba.druid.pool.DruidPooledStatement");
System.out.println(jarClazz);

6 遇到的问题

1. 如果一个类加载器已经加载了一个类,它再次调用defineClass()方法加载这个类时,会抛异常

Exception in thread "main" java.lang.LinkageError: loader (instance of  me/ffulauh/javalang/opbytecode/asm/simple/MyClassLoader): attempted  duplicate class definition for name: "me/ffulauh/javalang/opbytecode/asm/HelloWorld"at java.lang.ClassLoader.defineClass1(Native Method)at java.lang.ClassLoader.defineClass(ClassLoader.java:757)at java.lang.ClassLoader.defineClass(ClassLoader.java:636)at me.ffulauh.javalang.opbytecode.asm.simple.MyClassLoader.findClass(MyClassLoader.java:28)at me.ffulauh.javalang.opbytecode.asm.simple.MyClassLoader.loadClass(MyClassLoader.java:20)at me.ffulauh.javalang.opbytecode.asm.transform.ModifyMethodDemo.main(ModifyMethodDemo.java:38)

2. 在Tomcat 8中,处理请求的线程对应的类加载器为 TomcatEmbeddedWebappClassLoader,这个类加载器的 addURL方法是没有实现的。此时如果调用这个方法,是不会加载不到类的。


http://chatgpt.dhexx.cn/article/qnt72GOo.shtml

相关文章

【类加载器】java类加载器

类装载器ClassLoader&#xff08;一个抽象类&#xff09; 描述一下JVM加载class文件的原理机制 类装载器就是寻找类或接口字节码文件进行解析并构造JVM内部对象表示的组件&#xff0c;在java中类装载器把一个类装入JVM&#xff0c;经过以下步骤&#xff1a; 1、装载&#xff…

什么是类加载器?

类加载器 什么是类加载器&#xff0c;作用是什么&#xff1f; 类加载器就是加载字节码文件(.class)的类 Java语言是一种具有动态性的解释语言&#xff0c;类(CLASS) 只有被加载到 JVM 中后才能运行。当运行指定程序时&#xff0c;JVM会将编译生成的.class文件按照需求和一定的规…

类加载器

类加载过程 加载->连接->初始化。连接过程又可分为三步:验证->准备->解析。 类加载器分类 JVM 中内置了三个重要的 ClassLoader&#xff0c;除了 BootstrapClassLoader 其他类加载器均由 Java 实现且全部继承自java.lang.ClassLoader&#xff1a; 启动类加载器&…

类加载器作用

深入探讨 Java 类加载器 成 富, 软件工程师, IBM 中国软件开发中心 成富任职于 IBM 中国软件开发中心&#xff0c;目前在 Lotus 部门从事 IBM Mashup Center 的开发工作。他毕业于北京大学信息科学技术学院&#xff0c;获得计算机软件与理论专业硕士学位。他的个人网站是 http:…

java中的类加载器

文章目录 前言,一、加载器的作用是什么二、详解类加载器1.不得不说的双亲委派机制2.各个加载器加载的内容3.线程上下文类加载器4.类加载器的庐山真面目 总结 前言, java中一般来说有三种类加载器,分别为: 引导加载器,扩展加载器,应用加载器,还有一个线程上下文类加载器 一、加…

JVM类加载器

文章目录 一、类加载器二、类与类加载器三、双亲委派模型四、破坏双亲委派模型4.1、Tomcat4.1.1、WebApp类加载器4.1.2、Shared类加载器4.1.3、Catalina类加载器4.1.4、Common类加载器4.1.5、Jsp类加载器 4.2、JDBC 一、类加载器 从Java虚拟机的角度来讲&#xff0c;只存在两种…

自定义类加载器

目录 一、为什么要自定义类加载器&#xff1f; 二、常见的场景 三、实现方式 四、自定义类加载器示例 五、Java9新特性 一、为什么要自定义类加载器&#xff1f; 隔离加载类 在某些框架内进行中间件与应用的模块隔离&#xff0c;把类加载到不同的环境。比如&#xff1a;…

类加载器详解(自己实现类加载器)

目录&#xff1a; java虚拟机汇总 class文件结构分析 1).class文件常量池中的常量项结构 2). 常用的属性表的集合类加载过程 1).类加载器的原理以及实现<< 现在位置虚拟机结构分析 1).jdk1.7和1.8版本的方法区构造变化 2).常量池简单区分对象结构分析 1).压缩指针详解gc…

JVM——自定义类加载器

0. 为什么需要自定义类加载器 网上的大部分自定义类加载器文章&#xff0c;几乎都是贴一段实现代码&#xff0c;然后分析一两句自定义ClassLoader的原理。但是我觉得首先得把为什么需要自定义加载器这个问题搞清楚&#xff0c;因为如果不明白它的作用的情况下&#xff0c;还…

JVM - 类加载器

# 类加载器及类加载器执行过程 JDK版本&#xff1a;1.8 # 1、类加载器子系统 下图为类加载子系统&#xff1a; 类加载子系统负责从文件系统或者网络中加载class文件&#xff0c;class文件在文件开头有特定的文件标识&#xff08;CA FE BA BE&#xff09;。Classloader只负责cl…

java的类加载器以及如何自定义类加载器

ClassLoader作用 类加载流程的"加载"阶段是由类加载器完成的。 类加载器结构 结构&#xff1a;BootstrapClassLoader&#xff08;祖父&#xff09;–>ExtClassLoader&#xff08;爷爷&#xff09;–>AppClassLoader(也称为SystemClassLoader)&#xff08;爸…

类加载器(ClassLoader)

一、类加载器&#xff08;ClassLoader&#xff09; 1.1 什么是类加载器 Java的类加载器是Java虚拟机&#xff08;JVM&#xff09;的重要组成部分&#xff0c;它的主要作用是动态地将Java类加载到JVM中&#xff0c;以便在运行时使用这些类。Java类加载器通常是由JVM的实现者提供…

什么是类加载器,类加载器如何分类

一、类加载器 1.什么是类加载器 类加载器&#xff1a;负责将.class文件&#xff08;存储的物理文件&#xff09;加载到内存中 2.类加载时机&#xff1a; ① 创建类的实例&#xff08;对象&#xff09; ② 调用类的实例方法 ③ 访问类或者接口的类变量&#xff0c;或者为该…

深入理解Java虚拟机——再谈类的加载器——第十二章——中篇

深入理解Java虚拟机——Java虚拟机介绍——第一章 深入理解Java虚拟机——类加载子系统——第二章 深入理解Java虚拟机——运行时数据区和本地方法接口——详细篇——第三章 深入理解Java虚拟机——对象的实例化内存布局与访问定位——超级详细篇——第四章 深入理解Java虚拟机…

类加载器详解

类加载器的分类 JVM支持两种类型的类加载器,分别为引导类加载器(BootstrapClassLoader)和自定义类加载器(User-Defined ClassLoader) 从概念上来讲, 自定义类加载器一般指的是程序中由开发人员自定义的一类,类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类…

Java - 类加载器

文章目录 1. 类加载的过程2. 类加载器的分类2.1 引导类加载器 Bootstrap2.2 扩展类加载器 ExtClassLoader2.3 系统类加载器 AppClassLoader2.4 三者之间的关系2.5 自定义类加载器2.6 注&#xff1a;谁来准备类加载器呢? 3. 双亲委派机制4. ClassLoader抽象类5. URLClassLoader…

DAU 和 MAU

DAU 和 MAU 日活跃用户占月活跃用户的比例越高&#xff0c;表明用户对App的使用粘性越高。 DAU&#xff0c;即&#xff1a;Daily Active User&#xff0c;指日活跃用户数 MAU&#xff0c;即&#xff1a;Monthly Active User&#xff0c;指月活跃用户数。 *例子1&#xff1a;…

如何通过DAU分析活跃用户?(案例:python绘制箱体图)

前言&#xff1a;本文内容以游戏产品为基础进行讲解&#xff0c;内容为以下4部分&#xff1a; 1. 如何理解DAU反映了哪些问题&#xff1f; 2. 有哪些因素会影响DAU变动&#xff1f; 3. 如何解读DAU的“箱体图”&#xff1f; 4. 如何使用python绘制“箱体图”&#xff1f; DAU的…

【数据分析】产品日活DAU下降,怎么分析

目录 案例简介 第一步&#xff1a;确认数据真实性 第二步&#xff1a;明确定义&#xff0c;并拆解指标&#xff0c;进一步定位原异常部分 第三步&#xff1a;根据几个常见维度初步拆分数据 第四步&#xff1a;进一步做假设并细分深入&#xff0c;得出结论 案例分析 例题…

数据分析——DAU下降问题(转)

文章转自&#xff1a; DAU异常下降该如何分析 1. 梳理公司的用户增长模式 尽管不同业务形态、以及不同发展阶段的公司&#xff0c;其用户增长模式各有差异&#xff0c;但都可以从拉新策略和促活策略进行分解。 常见的拉新策略有&#xff1a; 流量采购。比如通过厂商预装、…