Java 类加载器

双亲委派模式(Parents Delegation Model)

Custom ClassLoader

加载器小结

Android 中的 ClassLoader

DexClassLoader

PathClassLoader

小结

相关推荐

Java 垃圾回收(GC)

Java 内存模型

Java JVM知识汇总

Java的四种引用方式


类加载器 ClassLoader

========================

Java 内存模型我们介绍了 Java 字节码文件(.class)的格式。一个完整的 Java 程序是由多个 .class 文件组成的,在程序运行过程中,需要将这些 .class 文件加载到 JVM 中才可以使用。而负责加载这些 .class 文件的就是类加载器(ClassLoader)。

Java 中的类何时被加载器加载

========================

在 Java 程序启动的时候,并不会一次性加载程序中所有的 .class 文件,而是在程序的运行过程中,动态地加载相应的类到内存中。

通常情况下,Java 程序中的 .class 文件会在以下 2 种情况下被 ClassLoader 主动加载到内存中:

  • 调用类构造器
  • 调用类中的静态(static)变量或者静态方法

Java 中 ClassLoader


JVM 中自带 3 个类加载器:

  • 启动类加载器 BootstrapClassLoader
  • 扩展类加载器 ExtClassLoader (JDK 1.9 之后,改名为 PlatformClassLoader)
  • 系统加载器 APPClassLoader

以上 3 者在 JVM 中有各自分工,但是又互相有依赖。

类加载机制


类的加载指将编译好的 Class 类文件中的字节码读入内存中,将其放在方法区内并创建对应的 Class 对象。类的加载分为加载、链接、初始化,其中链接又包括验证、准备、解析三步。如下图所示。

  • 加载是文件到内存的过程。通过类的完全限定名查找此类字节码文件,并利用字节码文件创建一个 Class 对象。

  • 验证是对类文件内容验证。目的在于确保 Class 文件符合当前虚拟机要求,不会危害虚拟机自身安全。主要包括四种:文件格式验证,元数据验证,字节码验证,符号引用验证。

  • 准备阶段是进行内存分配。为类变量也就是类中由 static 修饰的变量分配内存,并且设置初始值。这里要注意,初始值是 0 或者 null,而不是代码中设置的具体值,代码中设置的值是在初始化阶段完成的。另外这里也不包含用 final 修饰的静态变量,因为 final 在编译的时候就会分配。

  • 解析主要是解析字段、接口、方法。主要是将常量池中的符号引用替换为直接引用的过程。直接引用就是直接指向目标的指针、相对偏移量等。

  • 初始化,主要完成静态块执行与静态变量的赋值。这是类加载最后阶段,若被加载类的父类没有初始化,则先对父类进行初始化。

只有对类主动使用时,才会进行初始化,初始化的触发条件包括在创建类的实例时、访问类的静态方法或者静态变量时、Class.forName() 反射类时、或者某个子类被初始化时。

如上图所示,浅绿的两个部分表示类的生命周期,就是从类的加载到类实例的创建与使用,再到类对象不再被使用时可以被 GC 卸载回收。这里要注意一点,由 Java 虚拟机自带的三种类加载器加载的类在虚拟机的整个生命周期中是不会被卸载的,只有用户自定义的类加载器所加载的类才可以被卸载。

类加载器


如上图所示,Java 自带的三种类加载器分别是:BootStrap 启动类加载器、扩展类加载器和应用加载器(也叫系统加载器)。图右边的桔黄色文字表示各类加载器对应的加载目录。启动类加载器加载 java home 中 lib 目录下的类,扩展加载器负责加载 ext 目录下的类,应用加载器加载 classpath 指定目录下的类。除此之外,可以自定义类加载器。

BootstrapClassLoader 启动类加载器

它并不是使用 Java 代码实现的,而是由 C/C++ 语言编写的,它本身属于虚拟机的一部分。因此我们无法在 Java 代码中直接获取它的引用。

Bootstrap类加载器负责加载rt.jar 中的JDK类文件,它是所有类加载器的父加载器。Bootstrap类加载器没有任何父类加载器,如果尝试在 Java 层获 BootstrapClassLoader 的引用,系统会返回 null,任何基于此的代码会抛出NullPointerException 异常。Bootstrap加载器被称为初始类加载器。

ExtClassLoader 扩展类加载器

而Extension将加载类的请求先委托给它的父加载器,也就是Bootstrap,如果没有成功加载的话,再从 jre/lib/ext目录下或者java.ext.dirs系统属性定义的目录下加载类。Extension加载器由sun.misc.Launcher$ExtClassLoader 实现

APPClassLoader 系统类加载器

默认的加载器就是System类加载器(又叫作Application类加载器)了。它负责从classpath环境变量中加载某些应用相关的类,classpath环境变量通常由-classpath或-cp命令行选项来定义,或者是JAR中的Manifest的classpath 属性。Application类加载器是Extension类加载器的子加载器

双亲委派模式(Parents Delegation Model)

既然 JVM 中已经有了这 3 种 ClassLoader,那么 JVM 又是如何知道该使用哪一个类加载器去加载相应的类呢?答案就是:双亲委派模式

所谓双亲委派模式就是,当类加载器收到加载类或资源的请求时,通常都是先委托给父类加载器加载,也就是说,只有当父类加载器找不到指定类或资源时,自身才会执行实际的类加载过程。

注意:"双亲委派"机制只是 Java 推荐的机制,并不是强制的机制。我们可以继承 java.lang.ClassLoader 类,实现自己的类加载器。如果想保持双亲委派模型,就应该重写 findClass(name) 方法;如果想破坏双亲委派模型,可以重写 loadClass(name) 方法。

Custom ClassLoader

JVM 中预置的 3 种 ClassLoader 只能加载特定目录下的 .class 文件,如果我们想加载其他特殊位置下的 jar 包或类时(比如,我要加载网络或者磁盘上的一个 .class 文件),默认的 ClassLoader 就不能满足我们的需求了,所以需要定义自己的 Classloader 来加载特定目录下的 .class 文件。

自定义 ClassLoader 步骤:

  1. 自定义一个类继承抽象类 ClassLoader。
  1. 重写 findClass 方法。
  1. 在 findClass 中,调用 defineClass 方法将字节码转换成 Class 对象,并返回。

public class DiskClassLoader extends ClassLoader{

String filePath;

public DiskClassLoader(String filePath){

this.filePath = filePath;

}

@RequiresApi(api = Build.VERSION_CODES.O)

@Override

protected Class<?> findClass(String name) throws ClassNotFoundException {

String path  = filePath+name+“.class”;

byte [] classBytes = null;

try {

classBytes = Files.readAllBytes(Paths.get(new URI(path)));

} catch (IOException e) {

e.printStackTrace();

} catch (URISyntaxException e) {

e.printStackTrace();

}

return defineClass(name,classBytes,0,classBytes.length);

}

}

注意:如没有特殊的要求,一般不建议重写loadClass搜索类的算法。

加载器小结

Java 的类加载使用双亲委派模式,即一个类加载器在加载类时,先把这个请求委托给自己的父类加载器去执行,如果父类加载器还存在父类加载器,就继续向上委托,直到顶层的启动类加载器,如上图中蓝色向上的箭头。如果父类加载器能够完成类加载,就成功返回,如果父类加载器无法完成加载,那么子加载器才会尝试自己去加载。如图中的桔黄色向下的箭头。

这种双亲委派模式的好处,可以避免类的重复加载,另外也避免了 Java 的核心 API 被篡改。

Android 中的 ClassLoader

==============================

本质上,Android 和传统的 JVM 是一样的,也需要通过 ClassLoader 将目标类加载到内存,类加载器之间也符合双亲委派模型。但是在 Android 中, ClassLoader 的加载细节有略微的差别。

在 Android 虚拟机里是无法直接运行 .class 文件的,Android 会将所有的 .class 文件转换成一个 .dex 文件,并且 Android 将加载 .dex 文件的实现封装在 BaseDexClassLoader 中,而我们一般只使用它的两个子类:PathClassLoader 和 DexClassLoader。

DexClassLoader


先来看官方对 DexClassLoader 的描述:

A class loader that loads classes from {@code .jar} and {@code .apk} files containing a {@code classes.dex} entry. This can be used to execute code not installed as part of an application.

DexClassLoader 可以从 SD 卡上加载包含 class.dex 的 .jar 和 .apk 文件,这也是插件化和热修复的基础,在不需要安装应用的情况下,完成需要使用的 dex 的加载。

DexClassLoader 的源码里面只有一个构造方法,代码如下:

public class DexClassLoader extends BaseDexClassLoader {

public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {

super((String)null, (File)null, (String)null, (ClassLoader)null);

throw new RuntimeException(“Stub!”);

}

}

参数说明:

  • dexPath:包含 class.dex 的 apk、jar 文件路径 ,多个路径用文件分隔符(默认是":")分隔。

  • optimizedDirectory:此参数已弃用,自 API 级别 26 起无效。

  • librarySearchPath:C/C++ native 库的路径,多个路径用文件分隔符分隔; 可能是null。

  • parent:父类加载器

PathClassLoader


PathClassLoader 用来加载系统 apk 和被安装到手机中的 apk 内的 dex 文件。它的 2 个构造函数如下:

public class PathClassLoader extends BaseDexClassLoader {

public PathClassLoader(String dexPath, ClassLoader parent) {

super((String)null, (File)null, (String)null, (ClassLoader)null);

throw new RuntimeException(“Stub!”);

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-6VaHk7PQ-1715707257469)]

[外链图片转存中…(img-PGcmQja0-1715707257469)]

[外链图片转存中…(img-FS7TjbvN-1715707257469)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 30
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值