Android---深入理解ClassLoader的加载机制
目录
Java 中的 ClassLoader
1. APPClassLoader 系统类加载器
2. ExtClassLoader 扩展类加载器
3. BootstrapClassLoader 启动类加载器
双亲委派模式(Parents Delegation Model)
Android 中的 ClassLoader
1. PathClassLoader
2. DexClassLoader
总结
一个完整的 Java 程序是由多个 .class 文件组成的,在程序运行的过程中,需要将这些 .class 文件加载到 JVM 中才可以使用,而负责加载这些 .class 文件的就是类加载器(ClassLoader)。
Java 中的类何时被类加载器加载
Java 程序启动时,并不会一次性加载程序中所有的 .class 文件,而是在程序运行过程中,动态的加载相应的类到内存中。通常情况下,Java 程序中的 .class 文件会在以下两种情况下被 Class Loader 主动加载到内存:
1. 调用类构造器;
2. 调用类中的静态变量或者静态方法。
Java 中的 ClassLoader
JVM 中自带3个类加载器:
启动类加载器 BootstrapClassLoader;
扩展类加载器 ExtClassLoader(JDK1.9 之后,改名为 PlatformClassLoader);
系统类加载器 APPClassLoader。
1. AppClassLoader 系统类加载器
部分源码如下
可以看出 AppClassLoader 主要加载系统属性“java.class.path”配置下类文件,也就是环境变量 classpath 配置的路径下的文件。因此,AppClassLoader 是面向用户的类加载器。我们自己编写的代码以及使用的第三方 jar 包,通常都是由它来加载的。
2. ExtClassLoader 扩展类加载器
部分源码如下:
可以看出,扩展类加载器(ExtClassLoader )主要加载系统属性“java.ext.dirs”配置下类文件,可以通过如下代码打印出这个属性来查看具体有哪些文件。
System.out.println(System.getProperty("java.ext.dirs"));
3. BootstrapClassLoader 启动类加载器
BootstrapClassLoader 是由 C/C++ 语言编写的,它本身属于虚拟机的一部分,因此无法在 java 代码中直接获取它的引用。如果尝试在 Java 层获取 BootstrapClassLoader 的引用,系统会返回 null。
启动类加载器加载“sun.boot.class.path”配置下类文件,可以打印这个属性来查看具体有哪些文件
System.out.println(System.getProperty("sun.boot.class.path"));
结果如下:
可以看出这些全身 jre 目录下的的 jar 包或者 classes 文件。
双亲委派模式(Parents Delegation Model)
既然 JVM 中已经有了这3种 ClassLoader,那么 JVM 又是如何知道该使用哪一个类加载器去加载相应的类呢?答案这是:双亲委派模式。
双亲委派模式:当类加载器受到加载类或资源的请求时,通常都是先委托给父类加载器加载,只有当父类加载器找不到指定的类或资源时,自身才会执行实际的类加载过程。
其具体实现代码是在CLassLoader.java 中的 loadClass 方法中,如上图所示:
解释说明:上图中1处判断该 class 是否已经加载。如果已经加载,直接将该 class 返回;2处,如果该 class 没有被加载过,则判断 parent 是否为空,如果不为空则将加载的任务委托给 parent;3处,如果 parent 为空,则直接调用 BootstrapClass Loader 加载该类;4处,如果 parent 和 BootstrapClassLoader 都没有加载成功,则调用当前 ClassLoader 的 findClass() 方法继续尝试加载。
那么这个 parent 是什么呢?我们可以看 ClassLoader 的构造器,如下:
可以看出,在每一额 ClassLoader 中都有一个 ClassLoader 类型的 parent 引用,并且在构造器中传入值。继续查看源码,可以看到 AppClassLoader 传入的 parent 就是 ExtClassLoadr,而 ExtClassLoadr并没有传入任何 parent,也就是 null。
举例说明:
Test test = new Test();
默认情况下,JVM 首先调用 AppClassLoader 去加载 Test 类
1. APPClassLoader 将加载任务委派给它的父类加载器(parent)--ExtClassLoader;
2. ExtClassLoader 的 parent 为 null,则直接调用 BootstrapClassLoader 加载该类;
3. BootstrapClassLoader 在 jdk/jre 目录下无法找到 Test 类,因此返回的 Class 为 null;
4. 因为 parent 和 BootstrapClassLoader 都没能成功加载 Test 类,所以 AppClassLoader 会调用自身的 findClass() 方法来加载 Test 类。
最终,Test 就是被系统类加载器(AppClassLoader)加载到内存中的。
注意:“双亲委派”机制只是 Java 推荐的机制,并不是强制的机制。可以继承 java.lang.ClassLoader 类,实现自己的类加载器。如果想保持双亲委派模型,应该重写 findClass(name) 方法;如果想破坏双亲委派模型,可以重写 loadClass(name)方法。
自定义 ClassLoader
JVM 中预置的3种 ClassLoader 只能加载特定目录下的 .class 文件。如果想加载其它特殊位置下的 jar 包或类时(如网络或磁盘上),默认的 ClassLoader 就不能满足我们的需求。所以需要定义自己的 ClassLoader 来加载特定目录下的 .class 文件。
自定义 ClassLoader 步骤
1. 自定义一个类(MyClassLoader)继承抽象类 ClassLoader;
2. 重写 findClass 方法;
3. 在 findClass 方法中,调用 defineClass() 方法将字节码转换成 Class 对象,并返回。
自定义 ClassLoder 实践
1. 创建一个测试类 Secret.java,实现简单的打印功能。该文件存放在 D:\HL\ 目录下。在终端通过 javac 命令生成 Secret.class 文件。
public class Secret {public void printSecret() {System.out.println("I am a girl!");}
}
2. 创建 MyClassLoade 继承自 ClassLoader,重写 findClass() 方法。
public class MyClassLoader extends ClassLoader{private String filePath;public MyClassLoader(String path) {filePath = path;}@Overrideprotected Class<?> findClass(String name){// newPath = "D:\HL\Secret.class"// File.separator 来构建文件路径,以确保在不同操作系统上都能正常工作String newPath = filePath + File.separator + name.replace('.', File.separatorChar) + ".class";byte[] classBytes = null;try {Path path = Paths.get(newPath);classBytes = Files.readAllBytes(path);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}// 调用 defineClass 创建 class 并返回return defineClass(name, classBytes, 0, classBytes.length);}}
3. 测试
public class TestMyClassLoader {public static void main(String[] args) {// 创建自定义 ClassLoader 对象 // "D:/HL/" : 需要动态加载的 class 的路径MyClassLoader myClassLoader = new MyClassLoader("D:/HL/");try {//"software_test.Secret" 需要动态加载的类名Class c = myClassLoader.loadClass("software_test.Secret");if(c != null) {Object obj = c.newInstance();// 通过反射调用 Secret 的 printSecret 方法,即需要动态调用的方法名称Method method = c.getDeclaredMethod("printSecret", null);method.invoke(obj, null);}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
Android 中的 ClassLoader
本质上 Android 和传统的 JVM 是一样的,也需要 ClassLoader 将目标类加载到内存,类加载器之间也复合双亲委派模型。但是在 Android 中,ClassLoader的加载细节有略微的差别。在 Android 虚拟机里是无法直接运行 .class 文件,Android 中会将所有的 .class 文件转化为一个 .dex 文件。Android 将加载 .dex 文件的实现封装在 BaseDexClassLoader 中,一般我们使用它的两个子类:PathClassLoader 和 DexClassLoader。
1. PathClassLoader
用来加载系统 apk 和被安装到手机中 apk 内的 dex 文件。它的两个构造函数如下:
参数说明:
dexPath:dex 文件路径,或者包含 dex 文件的 jar 包路径;
librarySearchpath:C/C++ native 库路径。
PathClassLoader 代码里除了这两个构造函数外就没有其它代码了。具体的实现都是在 BaseDexClassLoader 里面。
当一个 app 被安装到手机后,apk 里面的 class.dex 中的 .class 均是通过 PathClassLoader 来加载的。
2. DexClassLoader
对比 PathClassLoader 只能加载已经安装的应用的 dex 或 apk 文件,DexClassLoader 则没有此限制,可以从 SD 卡上加载包含 class.dex 的 jar 包或者是 apk 文件。这也是插件化或热修复的基础,在不需要安装应用的情况下完成需要使用的 dex 文件的加载。
DexClassLoader 的源码里只有一个构造函数,如下:
参数说明:
dexPath:包含 class.dex 的 apk,jar 文件路径,多个路径使用文件分割符(默认是“:”)分隔;
optimizedDirectory:用来缓存优化的 dex 文件的路径,即从 apk 或 jar 文件中提取出来的 dex 文件,该路径不可以为空,且应该是私有的,有读写权限的路径。
总结
ClassLoader 是用来加载 class 文件的,不管是 jar 中还是 dex 中的 class;
Java 中的 ClassLoader 通过双亲委托来加载各自指定路径下的 class 文件;
可以自定义 ClassLoader,一般覆盖 findClass() 方法,不建议重写 loadClass 方法;
Android 中常用的两个 ClassLoader 分别为:PathClassLoader 和 DexClassLoader。
相关文章:
Android---深入理解ClassLoader的加载机制
目录 Java 中的 ClassLoader 1. APPClassLoader 系统类加载器 2. ExtClassLoader 扩展类加载器 3. BootstrapClassLoader 启动类加载器 双亲委派模式(Parents Delegation Model) Android 中的 ClassLoader 1. PathClassLoader 2. DexClassLoader 总结 一个完整的 Java…...

超自动化加速落地,助力运营效率和用户体验显著提升|爱分析报告
RPA、iPaaS、AI、低代码、BPM、流程挖掘等在帮助企业实现自动化的同时,也在构建一座座“自动化烟囱”。自动化工具尚未融为一体,协同价值没有得到释放。Gartner于2019年提出超自动化(Hyperautomation)概念,主要从技术组…...
Linux posix_spawn和fork的区别
posix_spawn和fork都是用于在Linux中创建新进程的函数,但它们的工作方式有所不同。posix_spawn它的工作方式类似于fork()后跟exec()。 fork:fork函数创建一个新的进程,该进程是调用进程的一个副本。这意味着除了必要的启动资源外,…...

聊聊分布式架构02——Http到Https
目录 HTTP通信协议 请求报文 响应报文 持久连接 状态管理 HTTPS通信协议 安全的HTTPS HTTP到HTTPS的演变 对称加密 非对称加密 混合加密机制 证书机构 SSL到底是什么 HTTPS是身披SSL外壳的HTTP HTTP通信协议 一次HTTP请求的通信流程:客户端浏览器通过…...

1024 画跳动的爱心#程序代码 #编程语言 #计算机
废话不多说 直接开干! 用到库 random time tkinter 快速镜像 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple tkinter 上代码 import random import time from math import sin, cos, pi, log from tkinter import *CANVAS_WIDTH 640 # 画布的宽 CANVAS_HEIGH…...

【排序算法】堆排序详解与实现
一、堆排序的思想 堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆(若不清楚什么是堆,可以看我前面的文章,有详细阐述)来进行选择数据&am…...

java Spring Boot整合jwt实现token生成
先在 pom.xml 文件中注入依赖 <!-- JWT --> <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.2</version> </dependency> <dependency><groupId>io.jsonw…...
如何使用Git和GitHub进行版本控制
如何使用Git和GitHub进行版本控制 版本控制是软件开发过程中的重要组成部分,它允许开发者跟踪和管理代码的变化,以确保团队协作顺畅,并帮助在需要时回溯到以前的代码状态。Git和GitHub是最流行的版本控制工具之一,本文将介绍如何…...
彻底解决 WordPress cURL error 28 错误
cURL 连接超时。 这种情况最普遍,这里的超时并不是完全不可连接,而是因为网络状况或其它原因数据传输缓慢,超过连接的时间限制导致传输中断引起的错误。 不论是何种原因导致连接超时,都可以通过增加超时限制来解决此问题。但 UR…...

LLM项目代码改写
背景: 最近在做代码大语言模型生成项目代码的课题。代码生成现在大部分的工作是在做即时代码生成,这个有点类似代码智能提示,只不过生成的可能是一段片段代码;然而对于整个项目代码的生成做的团队并不多,原因大致如下…...

小谈设计模式(14)—建造者模式
小谈设计模式(14)—建造者模式 专栏介绍专栏地址专栏介绍 建造者模式角色分类产品(Product)抽象建造者(Builder)具体建造者(Concrete Builder)指挥者(Director࿰…...
【kubernetes】k8s中的选主机制
leader-election选主机制 1 为什么需要leader-election? 在集群中存在某种业务场景,一批相同功能的进程同时运行,但是同一时刻,只能有一个工作,只有当正在工作的进程异常时,才会由另一个进程进行接管。这…...
学生选课系统基础版
第四章java中的集合框架 4.1:java中的集合框架概述 1.java概念与作用 现实中很多事物凑在一起都是集合 如购物车是商品的集合 军队呢 是军人的集合 学校是学生的结合 数学中的集合: 具有共同属性的事物的总体 java中的集合类呢 跟数学的集…...
redis no-appendfsync-on-rewrite
no-appendfsync-on-rewriteyes 当用户请求写入redis的时候,这部分数据只是保存在内存中,主线程并不会马上对此数据进行 aof刷盘(而是根据aof刷盘的频率由子线程进行同步),这样子不会阻塞但是会导致数据丢失no-appendfs…...

Spring Cloud Gateway2之路由详解
Spring Cloud Gateway路由 文章目录 1. 前言2. Gateway路由的基本概念3. 三种路由1. 静态路由2. 动态路由1. 利用外部存储2. API动态路由 3. 服务发现路由(自动路由)3.1. 配置方式3.2 自动路由(服务发现)原理核心源码GatewayDiscoveryClientAutoConfigur…...

阿里云RDS关系型数据库详细介绍_多版本数据库说明
阿里云RDS关系型数据库大全,关系型数据库包括MySQL版、PolarDB、PostgreSQL、SQL Server和MariaDB等,NoSQL数据库如Redis、Tair、Lindorm和MongoDB,阿里云百科分享阿里云RDS关系型数据库大全: 目录 阿里云RDS关系型数据库大全 …...

Vue中的数据绑定
一、v-bind单向数据绑定 单向数据绑定中,数据只能由data流向页面。 v-bind:属性名"data变量" 或简写为 :属性名"data变量" 我们修改data中的iptvalue值,页面input框中的value值改变。 而我们修改input框中的value值࿰…...

前后端分离计算机毕设项目之基于SpringBoot的旅游网站的设计与实现《内含源码+文档+部署教程》
博主介绍:✌全网粉丝10W,前互联网大厂软件研发、集结硕博英豪成立工作室。专注于计算机相关专业毕业设计项目实战6年之久,选择我们就是选择放心、选择安心毕业✌ 🍅由于篇幅限制,想要获取完整文章或者源码,或者代做&am…...
[JAVAee]Spring拦截器
适用场景 像是页面的登录验证处理,权限校验,登录日志的处理. 实现步骤 创建⾃定义拦截器,实现 HandlerInterceptor 接⼝的 preHandle(执⾏具体⽅法之前的预处理⽅法.将⾃定义拦截器加⼊ WebMvcConfigurer 的 addInterceptors ⽅法中. 下面以登录验证为例,实现拦…...

【nvm】Node Version Manager(NVM)安装配置以及使用(WIN版)
NVM 包管理工具 安装 访问NVM-Windows的GitHub页面:点击nvm-setup.exe。 根据提示进行下一步,文件位置选择自定义位置 验证安装是否成功 nvm version 。如果成功,它将显示NVM的版本号。 使用 nvm list available查看所有的可以被下载…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...

多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...

基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...