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

  • 时间:
  • 浏览:2
  • 来源:uu快3官网app_uu快3豹子赚钱

x怎么才能 才能 改进 Dalvik,缩短启动时间

/

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

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

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

*/

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

效果

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

76170: 2300 movs r4, #0

撤出 GC 例程函数采用钩子技术来实现,亲戚亲戚让让我们歌词 都将 GC 抑制封装成了三个 native 接口 doStartSuppressGCdoStopSuppressGC;但会 进一步封装为 JNI 接口,便于开发者在 Java 里调用。一般的应用法律法律依据 是,开发者通过日志看过支付宝在某个场景会触发多量的 GC 且你你这个 GC 影响用户体验(响应时间慢可能性动画卡顿),但会 在你你这个 场景前后插入 doStartSuppressGCdoStopSuppressGC

设计思路

本文里的设计只会用到一次 pre_hook,什么都有不发生性能问題。

看过的这里读者可能性会问,你你这个 通过“指令指纹”的法律法律依据 靠谱么?我的答案是,漏判不影响正确性,误判理论上发生但概率极小(误判指“指令指纹”定位到错误代码位置)。即使误判发生了,亲戚亲戚让让我们歌词 都还有最后一层保障——基础架构组同学实现的容灾机制。当误判导致 多线程 异常无法完成正常启动时,重启支付宝但会 在后续的启动中直接放弃 GC 抑制。

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

4. OOM 停止GC抑制的实现

GC 抑制的实现

撤出 GC 多线程 的唤醒

dvmSignalCond(&gHs->gcThreadCond);

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

撤出 softlimit 检测

{

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

1. 撤出 softlimit 检测:

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

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

撤出 GC 多线程 唤醒的目的是避免 GC 多线程 频繁唤醒导致 的多线程 抖动。下图是对应的 C++ 代码和 arm 指令片段,这段代码同样发生 dvmHeapSourceAlloc 函数中。在具体实现里亲戚亲戚让让我们歌词 都会依次扫描 libdvm.so 的 dynstr、dynsym、rel.plt 和 plt 区域获取 pthreadcondsignal@plt 的地址,但会 遍历 dvmHeapSourceAlloc 中的所有分支跳转,计算跳转目的地址。

前言

应用启动时间是移动 App 三个重要的用户体验环节,相对于普通的移动 App,支付宝过于庞大,必然会影响启动传输速率,但会 常规的优化手段在支付宝中可能性做得比较完善了,本篇文章尝试从 GC 的层面来进一步优化支付宝的启动传输速率。

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

支付宝是 Android 系统的三个应用多线程 ,怎么才能 才能 不能通过影响 Dalvik 的 GC 行为来缩短启动时间呢?你你这个 问題可不里能 分解为两步:

7616c: 42a1 cmp r1, r4

return NULL;

* We have exceeded the allocation threshold. Wake up the

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

* garbage collector.

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

以支付宝冷启动场景为例,亲戚亲戚让让我们歌词 都在容器 Quinox 的 attachBaseContext 函数里插入 doStartSuppressGC,在首页加载现在始于时插入 doStopSuppressGC

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严重影响应用启动时间的结论。

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

/*

第八个问題的难点在于投入产出比:修改多线程 空间的代码和数据是面向二进制,难度远远大于源代码,也只是说稍微复杂的 Dalvik 改进工作是可能性性的。

第三个问題答案是肯定的,Android 系统的设计思路是每个 Android 应用多线程 都在独立的 Dalvik 实例,应用启动可不里能 否修改当事人的多线程 空间里的代码和数据,但会 支付宝通过修改内存中的 Dalvik 库文件 libdvm.so 影响 Dalvik 的行为。

/

x条件满足时,撤出 钩子且执行从前的 dvmCollectGarbageInternal

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

本文作者:入弦

撤出 GC 例程函数

...

* if the heap is full.

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

}

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

背景

撤出 softlimit 检测的目的是最大限度的分配对象,下图为 softlimit 检查对应的 arm 指令片段,发生 dvmHeapSourceAlloc 函数中,OXE057 对应于"return NULL"的分支,可能性亲戚亲戚让让我们歌词 都想永远不进入"return NULL"分支,可不里能 改变 cmp 指令的结果,在具体实现里亲戚亲戚让让我们歌词 都把"0X42"作为"指令指纹"来识别但会 修改为 "cmp r0, r0",从前就可不里能 实现撤出 softlimit 检查。

3. 撤出 GC例程函数

76220: f7a9 ed0e blx 1fc40

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

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



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

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

OOM 停止 GC 抑制的实现

76224: 4620 mov r0, r4



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

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

void* dvmHeapSourceAlloc(size_t n)

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

可能性仅仅考虑在支付宝启动过程中抑制 GC,不前要考虑 OOM 停止 GC 抑制的实现,可能性支付宝启动缺乏以触发 OOM。但会 亲戚亲戚让让我们歌词 都希望 GC 抑制成为三个基础模块,不能应用到更多场景中。可能性多线程 在调用 doStopSuppressGC 前触发了 OOM,则前要在 OOM 发生前停止 GC 抑制。和前面简单的改变分支跳转方向不同,前要在 OOM 发生前注入三个新的的分支跳转,你你这个 新分支的代码由亲戚亲戚让让我们歌词 都来实现。新分支主要功能是,调用 doStopSuppressGC,但会 换成注入的新分支,最后跳回 Dalvik 执行 OOM。

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