【移动应用开发技术】Android中如何自定义ClassLoader耗时_第1页
【移动应用开发技术】Android中如何自定义ClassLoader耗时_第2页
【移动应用开发技术】Android中如何自定义ClassLoader耗时_第3页
【移动应用开发技术】Android中如何自定义ClassLoader耗时_第4页
【移动应用开发技术】Android中如何自定义ClassLoader耗时_第5页
已阅读5页,还剩3页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【移动应用开发技术】Android中如何自定义ClassLoader耗时

今天就跟大家聊聊有关Android中如何自定义ClassLoader耗时,可能很多人都不太了解,为了让大家更加了解,在下给大家总结了以下内容,希望大家根据这篇文章可以有所收获。首先看下西瓜目前使用的插件ClassLoader是怎么注入的,大致代码如下:代码大致意思是在PathClassLoader和BootClassLoader之间插入了一个DelegateClassLoader,而在DelegateClassLoader的findClass方法中去执行插件Class的加载。为了方便验证,写一个简单的测试Demo,测试加载一个类的耗时:以小米Max2,Android7.1.1机型为例,测试不注入和注入DelegateClassLoader加载一个类的耗时:不注入:60μs注入后:472μs不注入:60μs注入后:472μs差不多慢了8倍,测试了几款手机基本数据都差不多,但是4.x手机上这两种情况下耗时差别却很小。DelegateClassLoader.findClass耗时?因为双亲委托机制,所以宿主中所有类的加载都会走到DelegateClassLoader.findClass中,但是DelegateClassLoader中因为不存在宿主类,所以必然找不到,因此一个宿主类的加载会多调用了一次无用的findClass方法,一次findClass的调用会带来如此大的耗时?于是将DelegateClassLoader代码精简成下面这样的:这样,DelegateClassLoader中没有做任何插件类加载的逻辑,只是做了一个中转到父ClassLoader的loadClass的操作。结果依然是8倍左右的耗时差距。java方法调用耗时?上面方案里只是比不注入自定义ClassLoader多了一次DelegateClassLoader.loadClass方法的调用,理论上不可能存在这么大的耗时。如果说多调用一次java方法DelegateClassLoader.loadClass会有8倍的耗时差异的话,那么多调用两次是不是就是16倍的差异?于是尝试注入两个DelegateClassLoader,类似这样:但是结果还是8倍左右的耗时差异,并非16倍,这么说不是方法调用带来的性能损耗。自定义ClassLoader耗时?所以猜测可能是系统对PathClassLoader有什么优化?然后直接构造一个空的PathClassLoader注入到PathClassLoader和BootClassLoader中间,类似这样:神奇的8倍耗时差异没了!所以真的是系统对PathClassLoader有优化?带着这个疑问我们来看下ClassLoader的源码,以Android7.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里面就保存了已经加载过的类以及启动时从appimage中加载的类(appimage的作用是记录已经编译好的“热代码”,并且在启动时一次性把它们加载到缓存,参考Tinker博客)。如果一个类是首次加载且不在appimage中,那么这里会返回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/native/javalangVMClassLoader.cpp。重点来了!因为上面使用到了反射机制取PathClassLoader中的字段,为了保证这套机制不出问题,这里面加了个校验:如果ClassLoader链中存在不认识的ClassLoader,也就是说ClassLoader的类不是BootClassLoader和PathClassLoader,那么就认为加载类失败。当然这里加载失败的话,并不会影响最终类加载结果,因为在Java层findLoadedClass失败后,会走到findClass中的。结论在AndroidART中默认的ClassLoader机制,在ClassLoader#findLoadedClass时就把JVM中的findLoadedClass和findClass两件事情都做了。但是如果在classloader链中存在自定义ClassLoader,那么这个机制就会失效,会回

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论