0%

第12章:理解ClassLoader

热修复和插件化是目前比较热门的技术,想要更好地掌握它们需要先了解ClassLoader。从第11章可知,DVM 和 ART 加载的是dex文件,JVM 加载的是class文件,因此它们的类加载器 ClassLoader 是肯定有区别的。

Java中的ClassLoader

虚拟机章节提到 类加载子系统,它的主要作用就是通过多种类加载器来查找和加载Class文件到Java虚拟机中。Java系统中的类加载器主要包括以下:

  • Bootstrap ClassLoader(引导类加载器):C/C++实现的,用于加载指定的JDK核心类库,比如 java.lang、java.uti等系统类。
  • Extensions ClassLoader(拓展类加载器): Java中的实现类为 ExtClassLoader ,用于加载Java的拓展类,主要包括 $JAVA_HOME/jre/lib/ext 、java.ext.dir 等目录。
  • Application ClassLoader(应用程序类加载器):Java中的实现类为 AppClassLoader,用来加载 1、当前程序的 Classpath 目录 ;2、系统属性 java.class.path指定的目录。

ClassLoader 继承关系

以下代码可以验证 运行一个Java程序需要用到哪些类加载器:

1
2
3
4
5
6
7
8
9
public class ClassLoaderTest {
public static void main(String[] args){
ClassLoader loader = ClassLoaderTest.class.getClassLoader();
while (loader != null){
System.out.println(loader);//1
loader = loader.getParent();
}
}
}

上述代码可以获得当前类 ClassLoaderTest 的类加载器,接着打印出当前类加载器的父加载器,直到没有父加载器,打印的结果如:

sun misc Launcher AppClassLoader@75b84c92
sun .misc .Launcher$ExtClassLoader@lb6d3586

可以看出,加载 ClassLoaderTest 的加载器是 AppClassLoader,并且AppClassLoader 的父加载器是 ExtClassLoader。但是这里没有打印出 ExtClassLoader 的父加载器 Bootstrap ClassLoader ,是因为Bootstrap ClassLoader 由 C/C++ 编写,并不是一个Java类,因此我们无法在Java代码中获取。

双亲委派模型

所谓的双亲委派模型就是首先判断该Class是否已经加载,如果未加载,则当前加载器委托父加载器进行查找,这样依次地柜,直到委托到最顶层的 Bootstrap ClassLoader,如果Bootstrap ClassLoader 找到了该Class,就直接返回,否则,依次向下查找,如果当前加载器之上的所有加载器都未能加载,则当前加载器自身去查找。

如果要加载一个位于D盘的Class文件,系统所提供的类加载器就不能满足,这时候需要自定义类加载器 CustomClassLoader 继承java.lang.ClassLoader 并覆写findClass方法,加载D盘的Class文件步骤如下:

  1. CustomClassLoader 首先从缓存中查找Class文件是否已经加载,已经加载就返回,没有加载就委托给父加载器(AppClassLoader)
  2. 按照双亲委派模型递归。
  3. 一直委托到 Bootstrap ClassLoader ,如果 Bootstrap ClassLoader 也没能加载,则交给子加载器(ExtClassLoader),以此类推。

综合以上,ClassLoader的父子关系不是使用继承来实现的,二是使用组合来实现代码复用。

双亲委派模型的好处:

  1. 避免重复加载。如果Class已经加载过,就不需要加载,二是直接读取。
  2. 更加安全。如果不使用双亲委派模型,就可以自定义一个String类来替代系统的String类,显然会造成安全隐患。或者自定义一个Object类,有可能会动摇java基础,因为java里面所有类都要继承java的Object(这段是我自己理解添加的)。采用双亲委派模型似的系统的类在Java虚拟机启动时就被加载,也就无法自定义系统类来替代系统。

自定义类加载器的代码如下:
自定义类加载器1
自定义类加载器2

Android 中的ClassLoader

ClassLoader 的类型

Android中系统类加载器也主要包括3种:

  • BootClassLoader: 由Java代码实现,类的访问修饰符是默认的,只有在同一个包中才能访问,用户无法直接调用。Android系统启动时,会使用BootClassLoader 预加载常用类。
  • DexClassLoader:可以加在dex文件以及包含dex的压缩文件(apk和jar),不管加载哪种文件,最终都加载dex文件。
  • PathClassLoader:Android使用它来加载系统类和应用程序的类,通常用来加载已经安装的apk的dex文件。

ClassLoader的继承关系

通过前面用于验证java类继承关系的代码,在这里同样可以验证Android中类加载器的继承关系。

ClassLoader 的加载过程

Android 的 ClassLoader 同样遵循了双亲委派模型,ClassLoader 的加载方法为 loadClass方法,这个方法定义在抽象类 ClassLoader中。ClassLoader的查找流程如下图所示:

ClassLoader的查找流程

BootClassLoader的创建

在ZygoteInit的main方法中,调用了Zygote的 preload 方法,preload方法中又调用了 ZygoteInit 的 preloadClasses 方法,preloadClasses用于预加载常用的类,这个预加载属于拿空间换时间的策略。在preloadClasses方法中会创建 BootClassLoader 。

谢谢你的鼓励