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查看所有的可以被下载…...
【微服务】七. http客户端Feign
7.1 基于Feign远程调用 RestTimeplate方式调用存在的问题 先来看以前利用RestTemplate发起远程调用的代码: String url "http://userservice/user"order.getUserId(); User user restTemplate.getForObject(url,User.class);存在下面的问题…...
【Spring Boot 源码学习】OnWebApplicationCondition 详解
Spring Boot 源码学习系列 OnWebApplicationCondition 详解 引言往期内容主要内容1. getOutcomes 方法2. getMatchOutcome 方法3. isWebApplication 方法3.1 isServletWebApplication 方法3.2 isReactiveWebApplication 方法3.3 isAnyWebApplication 方法 总结 引言 上篇博文带…...
力扣之二分法
今天,学习了二分法,详细内容见代码随想录 (programmercarl.com),讲得十分好。 力扣之35. 搜索插入位置 - 力扣(LeetCode)。 class Solution { public:int searchInsert(vector<int>& nums, int target) {in…...
css图形化理解--扭曲函数skew()
transform: skewX(30deg);transform: skewY(45deg);transform: skew(30deg,45deg);transform: skewX(angleX);transform: skewY(angleY);transform: skew(angleX,angleY); 是CSS中的一个2D变换方法,它用于对元素沿X轴、Y轴进行倾斜变换。其中,angle表示倾…...
八、互联网技术——物联网
文章目录 一、智慧物联案例分析二、M2M技术三、数据保护综合案例分析一、智慧物联案例分析 智能物流是一种典型的物联网应用。一个物流仓储管理系统架构如下图所示: [问题1] 图中的三层功能:仓库物品识别、网络接入、物流管理中心,分别可对应到物联网基本架构中的哪一层? …...
聊聊MySQL的聚簇索引和非聚簇索引
文章目录 1. 索引的分类1. 存储结构维度2. 功能维度3. 列数维度4. 存储方式维度5. 更新方式维度 2. 聚簇索引2.1 什么是聚簇索引2.2 聚簇索引的工作原理 3. 非聚簇索引(MySQL官方文档称为Secondary Indexes)3.1 什么是非聚簇索引3.2 非聚簇索引的工作原理…...
python之subprocess模块详解
介绍 subprocess是Python 2.4中新增的一个模块,它允许你生成新的进程,连接到它们的 input/output/error 管道,并获取它们的返回(状态)码。 这个模块的目的在于替换几个旧的模块和方法。 那么我们到底该用哪个模块、哪个…...
第10讲:Vue组件的定义与注册
定义组件 1. 在程序的 components 目录下新建一个名为 Child.vue 的文件 2. 在文件内键入如下代码 <template><div>Child</div> </template> <script> export default {name: Child } </script>新建的 Child .vue 文件即为我们定义的组件…...
Pycharm操作git仓库 合并等
菜单 Git CommitPushUpdate ProjectPullFetchMergreRebase 查询 查询分支 查询本地所有分支 # 查询本地分支 git branch# 查询远程分支 git branch -rPycharm查看当前分支 步骤: Git->Branches 哈喽,大家好,我是[有勇气的牛排]&…...
Flink+Doris 实时数仓
Flink+Doris 实时数仓 Doris基本原理 Doris基本架构非常简单,只有FE(Frontend)、BE(Backend)两种角色,不依赖任何外部组件,对部署和运维非常友好。架构图如下 可以 看到Doris 的数仓架构十分简洁,不依赖 Hadoop 生态组件,构建及运维成本较低。 FE(Frontend)以 Java 语…...
2b2网站开发/百度移动端优化
sery 的BLOG链接:http://sery.blog.ccidnet.com/blog/ccid/do_showone/tid_35445.html来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/39335/viewspace-350917/,如需转载,请注明出处,否则将追究法律责任。 转载于…...
方维网站建设/营销策划的概念
分享讲师:王文广—达观数据副总裁,浙江大学计算机系硕士,在系统架构设计及研发方面有十余年工作经验。讲师简介:曾在百度负责MP3搜索的架构设计和开发,及百度第一版的语音搜索系统和百度音频指纹系统;曾负责…...
上海技术做网站/福州seo兼职
我的日食标志着每个带有惊叹号的新项目.我能够删除java 1.7并添加旧1.6但现在我收到此错误:java.lang.UnsupportedClassVersionError: klasse : Unsupported major.minor version 51.0at java.lang.ClassLoader.defineClass1(Native Method)at java.lang.ClassLoade…...
wordpress contactform/全球搜索引擎
结构体数组在颜色存储中的使用 什么是结构体数组? 结构体数组就是数组,用来装结构体变量的,那什么是结构体变量呢?结构体变量是用结构体类型定义的变量,那什么是结构体类型呢?如果不清楚你可移步往期文章…...
社区网站做的比较好的有哪些/优化大师优化项目有哪些
江苏省计算机一级考试复习资料第一章 信息技术概述一、关键点1信息处理指为获取有效信息而施加于初始信息全部操作。包含:信息搜集、加工、存放、传输、施用。基础信息技术: 感测和识别技术——扩展感觉器官功效 处理信息获取和识别通信和存放技术——扩…...
php网站建设制作方案/百度基木鱼建站
智慧警务三维电子沙盘系统 以“情报大数据、指挥扁平化、勤务可视化、情指勤一体化”为设计思想,整合三台合一、有无线通讯、12110短信平台、天网、交通平台、治安卡口、情报平台、一标三实、PGIS、GPS,采用大数据、云计算、三维GIS、移动互联网、物联…...