CrazyDailyQuestion icon indicating copy to clipboard operation
CrazyDailyQuestion copied to clipboard

2019-12-05: 说一下冷启动类加载的原理?为什么Tinker热修复需要重启应用生效?

Open MicroKibaco opened this issue 6 years ago • 2 comments

MicroKibaco avatar Dec 05 '19 03:12 MicroKibaco

冷启动类加载的原理

冷启动重启生效,现在一般有两种实现方式

  • 一种是类似于QQ空间的插桩,单独放一个帮助类在独立的dex中让其他类调用。阻止类被打上CLASS_ISPREVERIFIED 标志从而规避问题的出现.最后加载补丁dex得到dexFile对象作为参数构建一个Elements数组前面,这是QQ空间的做法

  • Tinker 提供dex差异包,整体替换dex方案,差量方式给出patch.dex,然后将patch.dex与应用的classs.dex合并成一个完整的dex,完整dex加载得到的dexFile对象作为参数构建Element对象然后整体替换掉原来旧的dex-Element数组,这样有个毛病就算是dex合并内存消耗在 vm heap 上,容易 OOM,最后导致dex合并失败。

dex merge 操作是在 Java 层面进行的,所有对象分配都是在 Java heap 上完成,如果进程申请超过 vm heap规定大小,那么进程就会发生OOM,系统 memory killer 可能会杀死该进程,导致dex合成失败。另外一方面,我们知道 JNI 层面 C++ new/malloc 申请的内存,分配在native heap 的增长并不受 vm heap 大小限制,只受限于RAM,如果RAM不足,导致进程会被杀死导致闪退。所以如果只是从 dex merge 方面考虑,理论上是可以的,但是实现起来非常复杂。

MicroKibaco avatar Dec 09 '19 13:12 MicroKibaco

为什么Tinker热修复需要重启应用生效?

  • dexopt/dexoat 操作是非常耗时的, 在Art 虚拟机影响非常大,因为loadDex 是补丁 dexAPK 中原 dex 合并成的一个完整的补丁压缩包,所以dexoat 操作非常耗时.如果优化后 odex 文件没有生成,或者生成不完整,那么loadDex 边不能在应用启动的时候进行,因为会阻塞loadDex进程,一般是主线程,所谓为了解决这个问题,我们把loadDex 当做一个事务来处理,如果中途打断,那么删除odex 文件,loadDex 完之后,反射注入/替换 dexElements 数组,实现打包。如果不存在odex 文件,那么重启另外一个子线程loadDex ,重启之后再生效

  • 补丁包安全性,对补丁包进行签名校验,这个时候防止补丁包被篡改,实际上虚拟机执行的是odex而不是dex,还需要进行odex文件进行校验进行MD5 完整性校验,如果匹配,则直接加载,如果不匹配,则重新生成odex文件,防止odex文件被篡改

MicroKibaco avatar Dec 10 '19 09:12 MicroKibaco