2.类加载器

article/2025/9/17 19:13:25

回顾

      上一节我们学习了JVM类加载机制,我们学习到大概的过程:通过类加载器将编译好的class文件加载到JVM进程中,通过字节码执行引擎去执行代码。这只是一个整体的过程,具体的细节我们从本节开始分析。

      通过本节我们将掌握以下知识:

  1. 类从加载到使用的整个过程
  2. 4种常用的类加载器
  3. 从源码角度分析类加载器初始化的全过程
  4. 双亲委派机制
  5. 自定义类加载器
  6. 打破双亲委派机制

本节内容干活满满,学完必有所收获!!!

1.类,从加载到使用

      类从加载到使用一共分为6个阶段:加载、验证、准备、解析、初始化、使用;我们详细分析以下每个阶段具体做了什么事情。

加载

      在用到某个类的时候,JVM会去class文件中将这个类加载到JVM中。如果是刚启动的程序,一般会以main()方法作为程序的主入口,把main()方法所在的类加载进来,然后从main() 方法开始执行代码,执行过程中用到哪个类,再去class文件中加载。

验证       

     根据Java虚拟机规范,校验加载进来的class文件是否符合指定的规范。所以把class文件加载到内存后,必须先验证以下,验证通过之后交给JVM虚拟机。

准备

    给加载到JVM虚拟机的类分配内存空间,并且会给类变量(static修饰)分配内存空间,并给默认值。

解析

    将符号引用转换为直接引用。

初始化

    类变量赋初始值,并执行静态代码块中的代码。

2.类加载器

   主要有4种类加载器:引导类加载器、扩展类加载器、应用程序类加载器、自定义类加载器。每种类加载器负责加载不同路径的类。

引导类加载器

Bootstrap ClassLoader,主要负责加载Java安装目录下,lib目录中的核心类库。

扩展类加载器

Extension ClassLoader,主要负责加载Java安装目录下,lib\ext目录下的类。

应用程序类加载器

Application ClassLoader,负责加载“ClassPath”环境变量所指定的路径中的类,就是我们自己写的类。

自定义类加载器

根据自己的需求,定义自己的类加载器。

3.类加载器初始化的全过程

   我们知道有4种类加载器,除了自定义的类加载器以外,我们需要知道其他3种类加载器是怎样来的。

① 在创建JVM进程时,通过Java.exe调用底层的jvm.dll文件创建Java虚拟机,并创建引导类加载器。

② C++调用Java代码创建JVM启动器实例Launcher类的getLauncher()方法。

③ 通过Launcher类的无参构造方法创建一个Launcher对象,在这个无参构造方法内依次创建了扩展类加载器、应用程序类加载器,并将应用程序类加载器设置为默认的类加载器。

public class Launcher {private static Launcher launcher = new Launcher();public static Launcher getLauncher() {return launcher;
}public Launcher() {Launcher.ExtClassLoader var1;try {// 创建扩展类加载器var1 = Launcher.ExtClassLoader.getExtClassLoader();} catch (IOException var10) {throw new InternalError("Could not create extension class loader", var10);}try {// 创建应用程序类加载器,并将应用程序类加载器设置为默认的类加载器this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);} catch (IOException var9) {throw new InternalError("Could not create application class loader", var9);}Thread.currentThread().setContextClassLoader(this.loader);String var2 = System.getProperty("java.security.manager");if (var2 != null) {SecurityManager var3 = null;if (!"".equals(var2) && !"default".equals(var2)) {try {var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();} catch (IllegalAccessException var5) {;} catch (InstantiationException var6) {;} catch (ClassNotFoundException var7) {;} catch (ClassCastException var8) {;}} else {var3 = new SecurityManager();}if (var3 == null) {throw new InternalError("Could not create SecurityManager: " + var2);}System.setSecurityManager(var3);}
}   
}
public ClassLoader getClassLoader() {return this.loader;}

4.双亲委派机制

4.1 什么是双亲委派机制

     上面说到的类加载器,是具有亲子结构的。当使用默认的类加载器-应用程序类加载器去加载某个类时,首先会委派给自己的父加载器-扩展类加载器去加载,扩展类加载器再委派给自己的父加载器-引导类加载器去加载。

     如果引导类加载器在自己负责的目录下没有找到对应的类,就会下推到自己的子加载器-扩展类加载器,子加载器再去自己负责的目录下查找,如果没有再次下推.....

     如果推到最下层依然没有找到,就会报错。

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

双亲委派机制有两个优点:

① 沙箱安全机制

例如:自己写的java.lang.String不会被加载,防止核心API库被篡改。

② 避免重复加载

当父亲已经加载过某个类时,子加载器就没有必要去加载了,保证一个类全局只加载一次。

4.3 双亲委派机制的源代码解析

双亲委派机制的源码在ClassLoader类的loadClass()方法内实现的。我们研究一下源码。

① 先调用findLoadClass()方法,根据包名看一下这个类是否已经被加载。

② 如果没有被加载过,就会走双亲委派的代码,一直递归的调用loadClass()方法,委托父加载器去加载。

③ 当父加载器为空时,即到了最顶层的引导类加载器,将用引导类加载器去加载这个类。

④ 如果启动器类加载器没有加载到,就调用findClass()方法去加载(向下查找),底层调用的是URLClassLoader去加载的。

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// 首先,检查一下这个类是否已经被加载Class<?> 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();// 调用URLClassLoader的findClass()方法在加载器的类路径里查找并加载该类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;}
}

5.自定义类加载器

ClassLoader类主要包含两个主要方法:loadClass()、findClass()方法。

  • loadClass():实现了双亲委派机制
  • findClass():默认为空,自定义类加载器就是实现这个方法即可。
/*** 自定义类加载器*/
public class MyClassLoader extends ClassLoader{private String classPath;public MyClassLoader(String classPath){this.classPath = classPath;}/*** findClass方法,参数为全类名* 思路:*     1.根据全类名找到对应的类,将这个类转换为字节数组*     2.调用defineClass()方法* @param name* @return* @throws ClassNotFoundException*/@SneakyThrows@Overrideprotected Class<?> findClass(String name){byte[] bytes = loadClassToByte(name);return defineClass(name, bytes, 0, bytes.length);}/*** 根据类全路径,找到对应类,转换为字节数组** @return*/private byte[] loadClassToByte(String name) throws IOException {name = name.replaceAll("\\.","/");// 根据全路径找到类,转换为输入流FileInputStream fileInputStream = new FileInputStream(classPath+"/"+name+".class");int available = fileInputStream.available();byte[] bytes = new byte[available];fileInputStream.read(bytes);fileInputStream.close();return bytes;}
}

使用自定义的类加载器 

public class JvmTest {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {MyClassLoader myClassLoader = new MyClassLoader("D:/tmp");Class<?> aClass = myClassLoader.loadClass("com.zzl.study.cloud.jvm.jvm.User");Object o = aClass.newInstance();Method method = aClass.getDeclaredMethod("usersay", null);Object invoke = method.invoke(o, null);System.out.println("类加载器"+aClass.getClassLoader().getClass().getName());}
}

6.打破双亲委派机制

打破双亲委派机制的核心在于:重写ClassLoader的loadClass()、findClass()方法。

/*** 打破双亲委派机制*   思路:*       1.重写loadClass()方法*       2.实现findClass()方法*/
public class MyClassLoader2 extends ClassLoader{private String classPath;public MyClassLoader2(String classPath){this.classPath = classPath;}@Overridepublic 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 t1 = System.nanoTime();// 特定的一批类,直接走自定义的findClass去加载不需要走双亲委派if (!name.startsWith("com.zzl.study")){c = this.getParent().loadClass(name);}else {c = findClass(name);}// this is the defining class loader; record the statssun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}if (resolve) {resolveClass(c);}return c;}}@SneakyThrows@Overrideprotected Class<?> findClass(String name) {byte[] bytes = loadClassToByte(name);return defineClass(name, bytes, 0, bytes.length);}/*** 根据类全路径,找到对应类,转换为字节数组** @return*/private byte[] loadClassToByte(String name) throws IOException {name = name.replaceAll("\\.","/");// 根据全路径找到类,转换为输入流FileInputStream fileInputStream = new FileInputStream(classPath+"/"+name+".class");int available = fileInputStream.available();byte[] bytes = new byte[available];fileInputStream.read(bytes);fileInputStream.close();return bytes;}
}
public class JvmTest {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {//测试打破双亲委派机制MyClassLoader2 myClassLoader2 = new MyClassLoader2("D:/tmp");Class<?> aClass = myClassLoader2.loadClass("com.zzl.study.cloud.jvm.jvm.User");Object o = aClass.newInstance();Method method = aClass.getDeclaredMethod("usersay", null);Object invoke = method.invoke(o, null);MyClassLoader2 myClassLoader3 = new MyClassLoader2("D:/tmp1");Class<?> aClass3 = myClassLoader3.loadClass("com.zzl.study.cloud.jvm.jvm.User");Object o3 = aClass3.newInstance();Method method3 = aClass3.getDeclaredMethod("usersay", null);Object invoke3 = method3.invoke(o3, null);}
}

http://chatgpt.dhexx.cn/article/5lVcciZN.shtml

相关文章

Java类加载器

一.类的生命周期 1. 加载&#xff08;Loading&#xff09;&#xff1a;找 Class 文件 1. 通过一个类的全限定名来获取定义此类的二进制字节流。 2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。 3.在内存中生成一个代表这个类的java.lang.Class对象&#xf…

Java类加载器的使用

Java类加载器 classloader顾名思义&#xff0c;即是类加载。虚拟机把描述类的数据从class字节码文件加载到内存&#xff0c;并对数据进行检验、转换解析和初始化&#xff0c;最终形成可以被虚拟机直接使用的Java类型&#xff0c;这就是虚拟机的类加载机制。 先认识一下类加载…

JVM 类加载器

什么是类加载器 类加载器负责在运行时将Java类动态加载到Java虚拟机&#xff0c;他们也是JRE&#xff08;Java运行时环境&#xff09;的一部分。因此&#xff0c;借助类加载器&#xff0c;JVM无需了解底层文件或文件系统即可运行Java程序。此外&#xff0c;这些Java类不会一次…

类加载器深入理解

虚拟机设计团队把类加载阶段中“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现&#xff0c;以便让应用程序自己决定如何去获取所需要的类。实现这个动作的模块称为“类加载器”。 类加载器在类层次划分、OSGI、热部署、代码加密等领域…

java获取类加载器

获取类加载器的方法: //扩展类加载器MainClassLoader classLoader MainTest.class.getClassLoader();//表示当前线程的类加载器——应用程序类加载器ClassLoader contextClassLoader Thread.currentThread().getContextClassLoader();//—启动类加载器ClassLoader systemClas…

类加载器的种类

类加载器的种类有四种&#xff0c;如下图所示&#xff1a; 1.启动类加载器&#xff08;引导类加载器&#xff0c;Bootstrap ClassLoader&#xff09; 这个类加载使用C/C语言实现的&#xff0c;嵌套在JVM内部它用来加载Java的核心库&#xff08;JAVA_HOME/jre/lib/rt.jar、res…

Java类加载器详解

1 特点 双亲委派&#xff1a; 如果一个类加载器收到了类加载的请求&#xff0c;它首先不会自己去尝试加载这个类&#xff0c;而是把这个请求委派给父类加载器去完成&#xff0c;每一个层次的类加载器都是如此&#xff0c;因此所有的加载请求最终都应该传送到顶层的启动类加载…

【类加载器】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;或者为该…