记录学习java
加载器学习所获心得,逐步记录了解java
加载器的过程。为了知悉android
插件化的实现原理,从而需要从头了解android
加载apk
,以及基础的java
类加载的加载过程情况,为方便记录和记忆,故此将学习了解的过程记录成文字,以下文字记录部分可能来自与多个来源,主体以Java
源码和IBM
开发者博客有关java
加载技术的博客为主。
Java 类的加载过程
惯性思维,想要了解apk
的加载过程,我希望先简单知道以下Java
中对类加载过程的处理(Java
一直都是android
的官方开发语言,虽然现在kotlin
也是,但原理不变)。首先需要了解Java
类在jvm
中的加载过程,基本的流程如下:
装载
装载是通过已经编译生成的class
文件的位置查找文件获取其字节流导入class
文件,并将其转换成一个Class
类的一个实例,将类的实例存放在jvm
的堆区,获取装载类的信息到方法区(属于jvm
的内存区域的一种,主要用于存放类的字段、方法,常量池等信息),这里就是负责处理完成类的加载的过程,主要是由ClassLoader
及其子类完成。
链接
其主要功能就是对类信息格式进行校验,分配方法区域的类变量的初始值(并非设置的初始化数值,而是初始“0”值)并将类的引用指向对应类的实例。其内部分为三个过程:校验 —>准备—>解析。执行顺序为既定的。
初始化
初始化类的静态变量和静态代码块(相对于链接中的准备阶段,将已经“初始化”的静态数据进行真正的初始化).类的初始化情况:
- 遇到类的创建指令
New
指令 java
主运行程序的入口类的实例- 通过反射创建类(
newInstance
、forClass
等) - 子类初始化触发父类的初始化操作
java 1.7
动态类型初始化
使用和卸载
对创建java
对象的操作以及java
回收机制对jvm
的自动回收卸载。
Java 类加载器
如上,对于java
类的加载使用,属于应用层程序员可控过程就只有类的加载过程,通过指定类的加载器来加载我们的类信息,首先通过java的源码文档来简单了解一下类加载的介绍。类加载器位置:java.lang.ClassLoader.java
文档介绍为:类加载器主要负责加载类的对象,通过给定一个类的“二进制名称”,那么类加载器会尝试定位或身成类定义的数据信息。一般策略是将二进制名称转化为一个文件的名称并加载该类文件的二进制数据。数组类型的类的对象并不是由类加载器创建,而是java
运行时根据需要自动创建.数组类型的加载器由Class.getClassLoader()
返回.该加载器与其元素类型的类加载器是相同的;如果该元素类型是基本类型,则该数组类没有类加载器。
程序可以通过继承ClassLoader
的子类来扩展动态加载方式.类加载器支持双亲委托模型(通过委托父类查找资源的方式进行操作)查找类或类的资源.虚拟机的内置类加载器(称为 “bootstrap class loader
”)本身没有父类加载器,但是可以将它用作 ClassLoader
实例的父类加载器……
如上,为部分对于ClassLoader
的介绍文字.对于通用类加载器通常将其分为四个类型,如下介绍:
引导型类加载器(bootstrap
)
该类加载器并没有父类类加载器,具体实现是通过原生代码实现(平台相关的),用于加载Java的核心代码,无法直接通过代码使用.
扩展型类加载器
它用来加载 Java
的扩展库。Java
虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java
类。android
中相当于java.lang.BootClassLoader
系统类加载器(system class loader
)
它根据 Java
应用的类路径(CLASSPATH
)来加载 Java
类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()
获取。android
中相当与dalvik.system.PathClassLoader
.
线程上下文类加载器
用于设置和获取线程上下文的类加载器,若是未曾设置该加载器,线程上下文类加载器将继承自父线的上下文类加载器,而Java
应用初始线程上下文类加载器为系统加载器,android
中也就是继承自BaseDexClassLoader
的子类加器PathClassLoader
或者DexClassLoader
加载器.
基于Android
平台对于以上的加载器做一个简单的代码测试如下:
var loader=classLoader
while (loader != null) {
println("加载器类型:${loader.toString()}")
loader = loader.parent
}
//输出结果如下
10-30 17:55:05.964 23338-23338/com.enjoytoday I/System.out: 加载器类型:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.enjoytoday-2/base.apk", zip file "/data/app/com.enjoytoday-2/split_lib_dependencies_apk.apk", zip file "/data/app/com.enjoytoday-2/split_lib_slice_0_apk.apk", zip file "/data/app/com.enjoytoday-2/split_lib_slice_1_apk.apk", zip file "/data/app/com.enjoytoday-2/split_lib_slice_2_apk.apk", zip file "/data/app/com.enjoytoday-2/split_lib_slice_3_apk.apk", zip file "/data/app/com.enjoytoday-2/split_lib_slice_4_apk.apk", zip file "/data/app/com.enjoytoday-2/split_lib_slice_5_apk.apk", zip file "/data/app/com.enjoytoday-2/split_lib_slice_6_apk.apk", zip file "/data/app/com.enjoytoday-2/split_lib_slice_7_apk.apk", zip file "/data/app/com.enjoytoday-2/split_lib_slice_8_apk.apk", zip file "/data/app/com.enjoytoday-2/split_lib_slice_9_apk.apk"],nativeLibraryDirectories=[/data/app/com.enjoytoday-2/lib/arm64, /system/fake-libs64, /data/app/com.enjoytoday-2/base.apk!/lib/arm64-v8a, /data/app/com.enjoytoday-2/split_lib_dependencies_apk.apk!/lib/arm64-v8a, /data/app/com.enjoytoday-2/split_lib_slice_0_apk.apk!/lib/arm64-v8a, /data/app/com.enjoytoday-2/split_lib_slice_1_apk.apk!/lib/arm64-v8a, /data/app/com.enjoytoday-2/split_lib_slice_2_apk.apk!/lib/arm64-v8a, /data/app/com.enjoytoday-2/split_lib_slice_3_apk.apk!/lib/arm64-v8a, /data/app/com.enjoytoday-2/split_lib_slice_4_apk.apk!/lib/arm64-v8a, /data/app/com.enjoytoday-2/split_lib_slice_5_apk.apk!/lib/arm64-v8a, /data/app/com.enjoytoday-2/split_lib_slice_6_apk.apk!/lib/arm64-v8a, /data/app/com.enjoytoday-2/split_lib_slice_7_apk.apk!/lib/arm64-v8a, /data/app/com.enjoytoday-2/split_lib_slice_8_apk.apk!/lib/arm64-v8a, /data/app/com.enjoytoday-2/split_lib_slice_9_apk.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64, /system/vendor/lib64, /product/lib64]]]
10-30 17:55:05.964 23338-23338/com.enjoytoday I/System.out: 加载器类型:java.lang.BootClassLoader@d725e7d
Android 中的类加载器
Android 中的虚拟机是由dalvik来实现,dalvik并非典型的Java虚拟机。因此,其类的加载器和标准有所不同,对于dalvik而言,其并不可以直接识别加载class文件,而是对class打包成的dex文件进行加载。因此,Android源码对ClassLoader进行处理,并派生一个子类BaseDexClassLoader,其本质类似于jvm中的ClassLoader,确切的说可以说是一个Dex加载器。
基于Android 平台的类加载器结构如下:
BootClassLoader
是属于ClassLoader
的一个内部类,不可直接使用,每个ClassLoader
中都存在一个parent(ClassLoader
类型),父类加载器,而BootClassLoader
属于最顶层的parent
.
URLClassLoader
这个是一个输入jar
的加载器,在java
中支持在线或本地指定jar
文件来加载jar
包,但由于android
中dalvik
并不可以识别class
或者jar
,只能加载dex
,所以并不可以直接使用URLClassLoader
来加载jar
文件
BaseDexClassLoader
这个属于加载dex
文件的加载器的实现,具体的加载逻辑在其中实现。
PathClassLoader
继承自BaseDexClassLoader
,为默认apk
安装使用的类加载器,会自动寻址apk
安装后默认解压后的dex
路径,目前dalvik
并不支持使用PathClassLoader
来加载未安装的apk
,但也有文章说art
可以实现,暂未验证,不能确认。
DexClassLoader
继承自BaseDexClassLoader
,可以直接加载dex
,压缩文件(apk
文件),jar
文件,是实现android
插件化一个重要的元素,可以帮我们完成对为安装的插件apk
的加载过程.