一、Java中的ClassLoader是什么?
当写好一个Java应用程序,程序都是由若干个.class类文件组织而成的,当程序在运行时,即会调用该程序的一个入口函数来调用系统的相关功能,而这些功能都被封装在不同的class文件当中,所以经常要从这个class文件中要调用另外一个class文件中的方法,如果另外一个文件不存在的,则会引发系统异常。而程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制(ClassLoader)来动态加载某个class文件到JVM中,从而只有class文件被载入到了JVM之后,才能被其它class所引用。所以ClassLoader就是用来动态加载class文件到JVM当中用的。
简单的说Java中的ClassLoader就是加载 class 文件,提供给程序运行时使用。
二、java中ClassLoader加载原理
1、原理介绍
ClassLoader使用的是双亲委托模型来搜索类的,每个ClassLoader实例都有一个父类加载器的引用(不是继承的关系,是一个包含的关系),虚拟机内置的类加载器(Bootstrap ClassLoader)本身没有父类加载器,但可以用作其它ClassLoader实例的的父类加载器。当一个ClassLoader实例需要加载某个类时。加载过程是:
1.源 ClassLoader 先判断该 Class 是否已加载,如果已加载,则直接返回 Class,如果没有则委托给父类加载器。
2.父类加载器判断是否加载过该 Class,如果已加载,则直接返回 Class,如果没有则委托给祖父类加载器。
3.依此类推,直到始祖类加载器(引用类加载器)。
4.始祖类加载器判断是否加载过该 Class,如果已加载,则直接返回 Class,如果没有则尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,则委托给始祖类加载器的子类加载器。
5.始祖类加载器的子类加载器尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,则委托给始祖类加载器的孙类加载器。
6.依此类推,直到源 ClassLoader。
7.源 ClassLoader 尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,源 ClassLoader 不会再委托其子类加载器,而是抛出异常。
2、JVM中是如何判定两个class是相同的呢?
JVM在判定两个class是否相同时,不仅要判断两个类名是否相同,而且要判断是否由同一个类加载器实例加载的。只有两者同时满足的情况下,JVM才认为这两个class是相同的。就算两个class是同一份class字节码,如果被两个不同的ClassLoader实例所加载,JVM也会认为它们是两个不同class。
同一个Class = 相同的 ClassName + PackageName + ClassLoader
如果想了解更加具体请看如下链接:
JVM 的工作原理,层次结构以及 GC 工作原理
深入分析Java ClassLoader原理
类加载机制:全盘负责和双亲委托
三、Android 中的 ClassLoader
Android 的 Dalvik(5.0之前版本)/ART(5.0增加的) 虚拟机如同标准 Java 的 JVM 虚拟机一样,也是同样需要加载 class 文件到内存中来使用,但是在 ClassLoader 的加载细节上会有略微的差别。
Android打包之后的apk 应用,将apk的后缀名改成zip或rar,解压之后会看见有一个或多个 class.dex 文件,用多个dex文件是android使用了MultiDexApplication解决65535的问题。
android在安装(installer)apk时,就对dex会进行验证和优化,同过一个专门的工具来处理,叫 DexOpt来进行处理,处理之后会产生ODEX文件,运行Apk的时候,直接加载ODEX,避免重复验证和优化,加快了Apk的响应时间。
总之,Android 中的 Dalvik/ART 无法像 JVM 那样直接加载 class 文件和 jar 文件中的 class,需要通过工具来优化转换成 Dalvik byte code 才行,只能通过 dex 或者包含 dex 的jar、apk 文件来加载(注意 odex 文件后缀可能是 .dex 或 .odex,也属于 dex 文件),因此 Android 中的 ClassLoader 工作就交给了 BaseDexClassLoader 来处理。
BaseDexClassLoader 及其子类
从ClassLoader的android源码看ClassLoader是一个abstract类,其具体实现的子类有BaseDexClassLoader和SecureClassLoader。
SecureClassLoader 的子类是URLClassLoader,其只能用来加载 jar 文件,这在 Android 的 Dalvik/ART 上没法使用的。
BaseDexClassLoader 的子类是PathClassLoader和DexClassLoader。
PathClassLoader
PathClassLoader 在应用启动时创建,从 data/app/… 安装目录下加载 apk 文件
dexPath:文件或者目录的列表,dex,apk,多个以文件分隔符分隔,默认是“.”
librarySearchPath:包含lib库的目录列表,多个以文件分隔符分隔,默认是空格
parent:父类加载器
PathClassLoader 里面除了这 2 个构造方法以外就没有其他的代码了,具体的实现都是在 BaseDexClassLoader 里面,其 dexPath 比较受限制,一般是已经安装应用的 apk 文件路径。
在 Android 中,App 安装到手机后,apk 里面的 class.dex 中的 class 均是通过 PathClassLoader 来加载的。
DexClassLoader
A class loader that loads classes from.jar and.apk files containing aclasses.dexentry. This can be used to execute code not installed as part of an application.
根据官方给出的文档 DexClassLoader 可以从 SD 卡上加载包含 class.dex 的 .jar 和 .apk 文件,这也是插件化和热修复的基础,在不需要安装应用的情况下,完成需要使用的 dex 的加载。
DexClassLoader类就只有一个方法,改方法有四个个参数
dexPath:dex文件路径列表,多个路径使用”:”分隔
dexOutputDir:经过优化的dex文件(odex)文件输出目录
libPath:动态库路径(将被添加到app动态库搜索路径列表中)
parent:这是一个ClassLoader,这个参数的主要作用是保留java中ClassLoader的委托机制(优先父类加载器加载classes,由上而下的加载机制,防止重复加载类字节码)
android的加载是根据BaseDexClassLoader子类进行加载不通的dex而子类的实现都在BaseDexClassLoader中,BaseDexClassLoader是关键,下次分析BaseDexClassLoader源码