Android自定义ClassLoader耗时问题追查

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

完整开源地址:https://docs.qq.com/doc/DSkNLaERkbnFoS0ZF

自定义ClassLoader耗时?

所以猜测可能是系统对 PathClassLoader 有什么优化?然后直接构造一个空的 PathClassLoader 注入到 PathClassLoader 和 BootClassLoader 中间,类似这样:

神奇的8倍耗时差异没了!所以真的是系统对 PathClassLoader 有优化?

带着这个疑问我们来看下 ClassLoader 的源码,以 Android 7.1.1 源码为例。

ClassLoader#loadClass

首先来看下源头,ClassLoader 的 loadClass 源码,核心代码如下:

大致流程是先调用 findLoadedClass 尝试从已加载的 class 中查找,然后再调用父 ClassLoader 的 loadClass 查找,如果依然没有找到的话,最后再调用自己的 findClass 加载。

在 JVM 中,类第一次加载时,肯定之前是没有加载过的,因此 findLoadedClass 应该是返回 null 的,而 BootClassLoader 中只有系统类,因此宿主类的加载应该是调用了 PathClassLoader#findClass 加载的。

PathClassLoader#findClass

那么我们再来看看 PathClassLoader#findClass 的源码,调用链大致如下:

如果说系统对 ClassLoader 有某些优化,那么应该只要重点关注在调用链中有用到 ClassLoader 的地方即可。

整个 findClass 流程中使用到 ClassLoader 的地方并不多,只有 ClassLinker::RegisterDexFile 和 ClassLinker::SetupClass 中使用到了。

  • ClassLinker::RegisterDexFile 中是对 ClassLoader 取 class_table 的简单操作;

  • ClassLinker::SetupClass 中是给加载好的 class 设置 ClassLoader,两个方法对 ClassLoader 的操作看上去是不存在任何优化的,理论上不会导致性能损耗,这里不再贴代码。

如果不是 findClass 里有优化,难道在 ClassLoader#findLoadedClass 里?

ClassLoader#findLoadedClass

再来看看 ClassLoader#findLoadedClass 的源码,调用链大致如下:

首先来看下c层调用的第一个方法 VMClassLoader_findLoadedClass :

这里主要有两个分支,第一个分支,第12行调用 ClassLinker#LookupClass :

这里大致意思是从 ClassLoader 中找到 ClassTable ,然后调用 ClassTable#Lookup 而这个 ClassTable 里面就保存了已经加载过的类以及启动时从 app image 中加载的类(app image的作用是记录已经编译好的“热代码”,并且在启动时一次性把它们加载到缓存,参考Tinker博客)。如果一个类是首次加载且不在 app image 中,那么这里会返回 null。

这样就会走到第二个分支(第25行) ClassLinker::FindClassInPathClassLoader 中

这里主要分为两个部分:

  • 第一部分:从37行开始,反射从 Java 层的 PathClassLoader 取得 DexPathList,然后再反射从 DexPathList 中取得 dexElements,然后再遍历 dexElements,从每个 Element 中取得 dexFile,然后再从 DexFile 中取得 mCookie,然后通过 mCookie 得到 c 层的 DexFile,最后调用 c 层 DexFile#FindClassDef 来真正的执行类的加载,整个流程其实就是在 c 层把 Java 层的 PathClassLoader#findClass 逻辑走了一遍;

  • 第二部分:采用递归的方式,从 BootClassLoader 开始依次到 PathClassLoader 逐个调用 FindClassInPathClassLoader,直到找到 class 为止,相当于把 Java 层 ClassLoader 的双亲委托加载 class 的机制在 c 层做了一遍,这个其实是 ART 上对 class 加载做的一个优化,但是在 Dalvik 中是没有这段逻辑的,可以参考/dalvik/vm/native/java_lang_VMClassLoader.cpp。

重点来了!因为上面使用到了反射机制取 PathClassLoader 中的字段,为了保证这套机制不出问题,这里面加了个校验:

  • 14
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值