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

  • 时间:
  • 浏览:0

76220: f7a9 ed0e blx 1fc40

return NULL;

* garbage collector.

*/

...

3. 注销GC例程函数

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

以支付宝冷启动场景为例,当当我们我们我们在容器 Quinox 的 attachBaseContext 函数里插入 doStartSuppressGC,在首页加载开使了了时插入 doStopSuppressGC

/

x支付宝算是能影响自身 Dalvik 的行为

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

注销 softlimit 检测的目的是最大限度的分配对象,下图为 softlimit 检查对应的 arm 指令片段,存在 dvmHeapSourceAlloc 函数中,OXE057 对应于"return NULL"的分支,不可能 当当我们我们我们想永远不进入"return NULL"分支,可不也能改变 cmp 指令的结果,在具体实现里当当我们我们我们把"0X42"作为"指令指纹"来识别已经 修改为 "cmp r0, r0",没人 就可不也能实现注销 softlimit 检查。

第八个间题报告 的难点在于投入产出比:修改应用应用tcp连接空间的代码和数据是面向二进制,难度远远大于源代码,也有些 说稍微复杂化的 Dalvik 改进工作是不不可能 的。

注销 softlimit 检测

x咋样改进 Dalvik,缩短启动时间

相对于 C 语言来说,Java 语言有有些特征,同类开发人员不不考虑内存的分配和回收,然而,应用应用tcp连接内存管理又是必不可少的环节,妥协的结果是 Java 语言的设计者们把对象分配和回收放上去了 Java虚拟机,这里希望明确没人 概念:GC 是有代价的,你你是什么代价包括:阻塞 Java 应用tcp连接的执行,占用 CPU 资源,占用额外内存等,谷歌的工程师意识到了 GC 对应用的影响,有些把 GC 的日志默认输出到了 Logcat,当当我们我们我们时不时也能想看 Logcat 里输出以下几种 GC 日志:

x条件满足时,注销钩子且执行没人 的 dvmCollectGarbageInternal

1. 注销 softlimit 检测:

注销 GC 应用tcp连接唤醒的目的是防止 GC 应用tcp连接频繁唤醒意味的应用tcp连接抖动。下图是对应的 C++ 代码和 arm 指令片段,这段代码同样存在 dvmHeapSourceAlloc 函数中。在具体实现里当当我们我们我们会依次扫描 libdvm.so 的 dynstr、dynsym、rel.plt 和 plt 区域获取 pthreadcondsignal@plt 的地址,已经 遍历 dvmHeapSourceAlloc 中的所有分支跳转,计算跳转目的地址。



GC 抑制的前提是 Dalvik 比较熟悉,知道咋样改变 GC 的行为,防止方案大致如下:首先在源码级别找到抑制GC的修改土辦法 ,同类改变跳转分支,其次,在二进制代码里找到 A 分支条件跳转的"指令指纹",以及用于改变分支的二进制代码,假设为 override_A,应用启动后扫描内存中的 libdvm.so,根据"指令指纹"定位到修改位置,已经 用 override_A 覆盖,这里必须注意的是,"指令指纹"的定义必须有有些编译器和 arm 指令集知识,实现 GC 抑制主要实现了以下 4 个次责:

效果

dvmSignalCond(&gHs->gcThreadCond);

本文作者:入弦

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

}

* We have exceeded the allocation threshold. Wake up the

本文里的设计只会用到一次 pre_hook,有些不存在性能间题报告 。

想看 的这里读者不可能 会问,你你是什么通过“指令指纹”的土辦法 靠谱么?我的答案是,漏判不影响正确性,误判理论上存在但概率极小(误判指“指令指纹”定位到错误代码位置)。即使误判存在了,当当我们我们我们还有最后一层保障——基础架构组同学实现的容灾机制。当误判意味应用tcp连接异常无法完成正常启动时,重启支付宝已经 在后续的启动中直接放弃 GC 抑制。

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

OOM 停止 GC 抑制的实现

背景

设计思路

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

支付宝是 Android 系统的没人 应用应用tcp连接,咋样也能通过影响 Dalvik 的 GC 行为来缩短启动时间呢?你你是什么间题报告 可不也能分解为两步:

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

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

void* dvmHeapSourceAlloc(size_t n)

注销 GC 例程函数采用钩子技术来实现,当当我们我们我们将 GC 抑制封装成了没人 native 接口 doStartSuppressGCdoStopSuppressGC;已经 进一步封装为 JNI 接口,便于开发者在 Java 里调用。一般的应用土辦法 是,开发者通过日志想看 支付宝在某个场景会触发少量的 GC 且你你是什么 GC 影响用户体验(响应时间慢不可能 动画卡顿),已经 在你你是什么场景前后插入 doStartSuppressGCdoStopSuppressGC

前言

/*

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

7621e: 50b4 adds r0, #150 ; 0xb4

/

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

GC_FOR _ALLOCK:是分配对象失败时触发的 GC,你你是什么 GC 会将应用所有的 Java 应用tcp连接暂停运行,直到 GC 开使了了。GC_CONCURRENT:是 Java 虚拟机根据堆的当前情况触发的 GC,你你是什么 GC 在 Dalvik 单独 GC 应用tcp连接里运行,在次责时间里不影响应用 Java 应用tcp连接的运行。

支付宝启动是没人 典型的关键路径场景,当当我们我们我们希望想看 尽不可能 少的 GC_ CONCURRENT(不可能 不可能 ,GC_ FOR_ ALLOCK 也应该缩减到相当于),然而,通过 Logcat 当当我们我们我们会想看 非常糟糕的 GC 行为—少量的 GC_ FOR_ ALLOCK 以及触目惊心的 Java 应用tcp连接被 WAIT_ FOR_ CONCURRENT_ GC 阻塞,如下图所示,通过简单统计那些GC消耗的时间,当当我们我们我们也能得出GC严重影响应用启动时间的结论。

不可能 仅仅考虑在支付宝启动过程中抑制 GC,不不须考虑 OOM 停止 GC 抑制的实现,不可能 支付宝启动不足英文以触发 OOM。已经 当当我们我们我们希望 GC 抑制成为没人 基础模块,也能应用到更多场景中。不可能 应用tcp连接在调用 doStopSuppressGC 前触发了 OOM,则必须在 OOM 存在前停止 GC 抑制。和前面简单的改变分支跳转方向不同,必须在 OOM 存在前注入没人 新的的分支跳转,你你是什么新分支的代码由当当我们我们我们来实现。新分支主要功能是,调用 doStopSuppressGC,已经 去掉 注入的新分支,最后跳回 Dalvik 执行 OOM。

7616c: 42a1 cmp r1, r4

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



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

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

76170: 250 movs r4, #0

第没人 间题报告 答案是肯定的,Android 系统的设计思路是每个 Android 应用应用tcp连接都有独立的 Dalvik 实例,应用启动可不也也能修改买车人的应用应用tcp连接空间里的代码和数据,已经 支付宝通过修改内存中的 Dalvik 库文件 libdvm.so 影响 Dalvik 的行为。

注销 GC 例程函数

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

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

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

{

4. OOM 停止GC抑制的实现

76224: 4620 mov r0, r4

* if the heap is full.

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

GC 抑制的实现

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

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

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

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

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

注销 GC 应用tcp连接的唤醒

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