Android 客户端启动速度优化之「垃圾回收」

  • 时间:
  • 浏览:1

撤销 GC 例程函数

第还还有一个大现象的难点在于投入产出比:修改系统进程空间的代码和数据是面向二进制,难度远远大于源代码,也可是我说稍微复杂的 Dalvik 改进工作是不机会的。

x支付宝否有 能影响自身 Dalvik 的行为

本节将介绍支付宝 Android 客户端启动时延优化下的「垃圾回收」具体思路。

上图的启动时间的数据是在外部的 Android 4.x 测试设备上获得的(没办法 标注 release 表示 debug 版本)。从图表上来看,支付宝客户端的启动时间缩短了 15%~300%。

撤销 softlimit 检测

本文里的设计只会用到一次 pre_hook,全都不趋于稳定性能大现象。

看了的这里读者机会会问,你你是什么 通过“指令指纹”的最好的法律法律依据靠谱么?我的答案是,漏判不影响正确性,误判理论上趋于稳定但概率极小(误判指“指令指纹”定位到错误代码位置)。即使误判趋于稳定了,大伙还有最后一层保障——基础架构组同学实现的容灾机制。当误判原应 系统进程异常无法完成正常启动时,重启支付宝而且在后续的启动中直接放弃 GC 抑制。

撤销 GC 系统进程的唤醒

*/

OOM 停止 GC 抑制的实现

基于以上两点,提出了本身设想:启动时 GC 抑制,允许堆时不时增长,直到开发人员主动停止 GC 抑制机会 OOM 停止 GC 抑制,这是本身"空间换时间"策略,用更多的内存消耗来换取启动时间的缩短,你你是什么 策略可行另另有一一还还有一个 多多前提:一是设备厂商没办法 加密内存中的 Dalvik 库文件,二是设备厂商没办法 改动 Google 的 Dalvik 源码(机会一定量的改动),理论上通过白名单的最好的法律法律依据可不也能覆盖所有设备,而且实现和维护成本都非常高。

7616c: 42a1 cmp r1, r4

《支付宝客户端架构解析》系列将从支付宝客户端的采集方案入手,细分拆解客户端在“容器化框架设计”、“网络优化”、“性能启动优化”、“自动化日志采集”、“RPC 组件设计”、“移动应用监控、诊断、定位”等具体实现,带领大伙进一步了解支付宝在客户端架构上的迭代与优化历程。

7621e: 300b4 adds r0, #13000 ; 0xb4

应用启动时间是移动 App 另另有一一还还有一个 多重要的用户体验环节,相对于普通的移动 App,支付宝过于庞大,必然会影响启动时延,你你是什么 常规的优化手段在支付宝中机会做得比较完善了,本篇文章尝试从 GC 的层面来进一步优化支付宝的启动时延。

相对于 C 语言来说,Java 语言有你你是什么 社会形态,累似 开发人员不要考虑内存的分配和回收,然而,系统进程内存管理又是必不可少的环节,妥协的结果是 Java 语言的设计者们把对象分配和回收插进了 Java虚拟机,这里希望明确另另有一一还还有一个 多概念:GC 是有代价的,你你是什么 代价包括:阻塞 Java 系统进程的执行,占用 CPU 资源,占用额外内存等,谷歌的工程师意识到了 GC 对应用的影响,全都把 GC 的日志默认输出到了 Logcat,大伙时不时也能看了 Logcat 里输出以下几种 GC 日志:

76174: f8df 90bc ldr.w r9, [pc, #188] ; 76234 <_Z18dvmHeapSourceAllocj+0xe0>

x如何改进 Dalvik,缩短启动时间

GC_FOR _ALLOCK:是分配对象失败时触发的 GC,你你是什么 GC 会将应用所有的 Java 系统进程暂停运行,直到 GC 现在结束了了了。GC_CONCURRENT:是 Java 虚拟机根据堆的当前状况触发的 GC,你你是什么 GC 在 Dalvik 单独 GC 系统进程里运行,在每段时间里不影响应用 Java 系统进程的运行。

支付宝启动是另另有一一还还有一个 多典型的关键路径场景,大伙希望看了尽机会少的 GC_ CONCURRENT(机会机会,GC_ FOR_ ALLOCK 也应该缩减到为宜),然而,通过 Logcat 大伙会看了非常糟糕的 GC 行为—一定量的 GC_ FOR_ ALLOCK 以及触目惊心的 Java 系统进程被 WAIT_ FOR_ CONCURRENT_ GC 阻塞,如下图所示,通过简单统计哪几个GC消耗的时间,大伙也能得出GC严重影响应用启动时间的结论。

支付宝是 Android 系统的另另有一一还还有一个 多应用系统进程,如何也能通过影响 Dalvik 的 GC 行为来缩短启动时间呢?你你是什么 大现象可不也能分解为两步:

void* dvmHeapSourceAlloc(size_t n)

* This allocation would push us over the soft limit; act as

GC 抑制的实现

以支付宝冷启动场景为例,大伙在容器 Quinox 的 attachBaseContext 函数里插入 doStartSuppressGC,在首页加载现在结束了了了时插入 doStopSuppressGC

/

撤销 GC 例程函数采用钩子技术来实现,大伙将 GC 抑制封装成了另另有一一还还有一个 多 native 接口 doStartSuppressGCdoStopSuppressGC;而且进一步封装为 JNI 接口,便于开发者在 Java 里调用。一般的应用最好的法律法律依据是,开发者通过日志看了支付宝在某个场景会触发一定量的 GC 且你你是什么 GC 影响用户体验(响应时间慢机会动画卡顿),而且在你你是什么 场景前后插入 doStartSuppressGCdoStopSuppressGC

* We have exceeded the allocation threshold. Wake up the

7621c: 630000 ldr r0, [r0, #0]

if (heap->bytesAllocated + n > hs->softLimit) {

* garbage collector.

7617e: 7d1a ldrb r2, [r3, #20]

实现中使用了开源的二进制注入框架:https://github.com/crmulliner/adbi 。



76224: 4620 mov r0, r4

背景

x当条件不满足时直接返回,达到撤销 GC 的目的;

设计思路

76172: e057 b.n 76224 <_Z18dvmHeapSourceAllocj+0xd0>

GC 抑制的前提是 Dalvik 比较熟悉,知道如何改变 GC 的行为,处置方案大致如下:首先在源码级别找到抑制GC的修改最好的法律法律依据,累似 改变跳转分支,其次,在二进制代码里找到 A 分支条件跳转的"指令指纹",以及用于改变分支的二进制代码,假设为 override_A,应用启动后扫描内存中的 libdvm.so,根据"指令指纹"定位到修改位置,而且用 override_A 覆盖,这里需要注意的是,"指令指纹"的定义需要有你你是什么 编译器和 arm 指令集知识,实现 GC 抑制主要实现了以下 4 个每段:

/*

3. 撤销GC例程函数

76220: f7a9 ed0e blx 1fc40

机会仅仅考虑在支付宝启动过程中抑制 GC,需要考虑 OOM 停止 GC 抑制的实现,机会支付宝启动过高 以触发 OOM。而且大伙希望 GC 抑制成为另另有一一还还有一个 多基础模块,也能应用到更多场景中。机会系统进程在调用 doStopSuppressGC 前触发了 OOM,则需要在 OOM 趋于稳定前停止 GC 抑制。和前面简单的改变分支跳转方向不同,需要在 OOM 趋于稳定前注入另另有一一还还有一个 多新的的分支跳转,你你是什么 新分支的代码由大伙来实现。新分支主要功能是,调用 doStopSuppressGC,而且加进注入的新分支,最后跳回 Dalvik 执行 OOM。

原文发布时间为:2018-11-27

这里需要注意的是,在热点函数里使用你你是什么 框架提供的 pre_hookpost_hook 的性能开销非常大。



前言

7616e: d901 bls.n 76174 <_Z18dvmHeapSourceAllocj+0x20>

效果

dvmSignalCond(&gHs->gcThreadCond);

撤销 GC 系统进程唤醒的目的是处置 GC 系统进程频繁唤醒原应 的系统进程抖动。下图是对应的 C++ 代码和 arm 指令片段,这段代码同样趋于稳定 dvmHeapSourceAlloc 函数中。在具体实现里大伙会依次扫描 libdvm.so 的 dynstr、dynsym、rel.plt 和 plt 区域获取 pthreadcondsignal@plt 的地址,而且遍历 dvmHeapSourceAlloc 中的所有分支跳转,计算跳转目的地址。

实现同样采用传统的钩子技术。在钩子函数 dvmCollectGarbageInternal 里:

return NULL;

* if the heap is full.

{

本文作者:入弦

}

4. OOM 停止GC抑制的实现

76226: e8bd 83f8 ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, pc}

76178: 6a28 ldr r0, [r5, #32]

/

1. 撤销 softlimit 检测:

...

机会发现 pthreadcondsignal@plt 和当前分支跳转目的地址配置,擦除这条指令即可。

76170: 2300 movs r4, #0

7617a: f853 30009 ldr.w r3, [r3, r9]

第另另有一一还还有一个 多大现象答案是肯定的,Android 系统的设计思路是每个 Android 应用系统进程也有独立的 Dalvik 实例,应用启动可不也还还都里能修改被委托人的系统进程空间里的代码和数据,而且支付宝通过修改内存中的 Dalvik 库文件 libdvm.so 影响 Dalvik 的行为。

撤销 softlimit 检测的目的是最大限度的分配对象,下图为 softlimit 检查对应的 arm 指令片段,趋于稳定 dvmHeapSourceAlloc 函数中,OXE057 对应于"return NULL"的分支,机会大伙想永远不进入"return NULL"分支,可不也能改变 cmp 指令的结果,在具体实现里大伙把"0X42"作为"指令指纹"来识别而且修改为 "cmp r0, r0",原先就可不也能实现撤销 softlimit 检查。

x条件满足时,撤销钩子且执行原先的 dvmCollectGarbageInternal

if (heap->bytesAllocated > heap->concurrentStartBytes) {