乐趣区

关于java:iOS-Abort问题系统性解决方案

一、背景

解体 (Crash),即闪退,多指挪动设施(如 iOS、Android 设施)在关上 / 应用应用程序的过程中,忽然出现意外退出 / 中断的状况。如果 App 线上版本频繁产生解体,会极大地影响用户体验,甚至导致用户散失,以及收益缩小。因而,解体问题是客户端稳定性团队须要重点解决的问题。

然而,对于所有解体场景,仅 25% 的解体可通过信号量捕捉,施行相应改良;另有 75% 的解体则难以辨认,从而对 App 的用户体验,造成了微小的潜在影响。

Facebook 的工程师将 App 退出分为以下 6 个类别:
1.App 外部被动调用 exit() 或 abort() 退出;
2.App 降级过程中,用户过程被杀死;
3. 系统升级过程中,用户过程被杀死;
4.App 在后盾被杀死;
5.App 在前台被杀死,且可获取堆栈;
6.App 在前台被杀死,且无奈获取堆栈。

对于第 1~4 类退出,属于 App 的失常退出,对用户体验没有太大影响,无需进行相应解决;对于第 5 类退出,可通过堆栈代码级定位解体起因,对此业界已造成比拟成熟的解决方案,举荐收费试用阿里云的解体剖析服务,即可疾速定位、解决此类解体问题;对于第 6 类退出,可能的起因很多,包含但不限于:零碎内存不足时持续申请内存、主线程卡死 20s 以上、CPU 使用率过高 Stack Overflow 等,在此咱们对立称之为 iOS 客户端的“Abort 问题”。

Abort 问题无奈被堆栈捕捉,且产生频次远高于可被捕捉的解体(下称“堆栈解体”)。从历史数据来看,手淘(电商类超级 App 代表)的 Abort 问题数量个别是堆栈解体数量的 3 倍左右;优酷 Pad(视频类超级 App 代表)的 Abort 问题数量个别是堆栈解体数量的 5 倍左右。可见,Abort 问题对用户的应用体验造成微小影响。

本文将针对 iOS 客户端的 Abort 问题,进行根因定位剖析,并提出系统性解决方案。

二、Abort 问题的起因分类

造成 Abort 问题的起因次要包含以下 4 个。

2.1 内存 Jetsam

挪动端设施的物理内存资源缓和,但 App 仍一直申请内存。因而零碎 signal 9 杀死过程,造成异样退出。

{   
"memoryPages" : {  
   "active" : 24493,  
   "throttled" : 0,  
   "fileBacked" : 24113,  
   "wired" : 13007,  
   "anonymous" : 12915,  
   "purgeable" : 127,  
   "inactive" : 10955,  
   "free" : 2290,  
   "speculative" : 1580  
},  
"uncompressed" : 125795,  
"decompressions" : 143684  
},  
"largestProcess" : "Taobao4iPhone",  
"processes" : [  
{  
...  
{  
   "rpages" : 2050,  
   "states" : [  
     "frontmost",  
     "resume"  
   ],  
   "name" : "Taobao4iPhone",  
   "pid" : 1518,  
   "reason" : "vm-thrashing",  
   "fds" : 50,  
   "uuid" : "5103a88a-917f-319e-8553-c0189dd1abac",  
   "purgeable" : 127,  
   "cpuTime" : 4.619693,  
   "lifetimeMax" : 3557  
},  
...  
}

2.2 主线程死锁

A/ B 两个线程同时期待对方实现某些操作,因此无奈继续执行,造成死锁,造成异样退出。

Exception Type:  00000020
Exception Codes: 0x000000008badf00d
Highlighted Thread:  0
 
Application Specific Information:
com.myapp.myapp failed to scene-create in time
 
Elapsed total CPU time (seconds): 4.230 (user 4.230, system 0.000), 10% CPU 
Elapsed application CPU time (seconds): 1.039, 3% CPU
 
Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0:
0   libsystem_kernel.dylib          0x36360540 semaphore_wait_trap + 8
1   libdispatch.dylib               0x36297eee _dispatch_semaphore_wait_slow + 186
2   libxpc.dylib                    0x364077b8 xpc_connection_send_message_with_reply_sync + 152
3   Security                        0x2b8dd310 securityd_message_with_reply_sync + 64
4   Security                        0x2b8dd48c securityd_send_sync_and_do + 44
5   Security                        0x2b8ea452 __SecItemCopyMatching_block_invoke + 166
6   Security                        0x2b8e96f6 SecOSStatusWith + 14
7   Security                        0x2b8ea36e SecItemCopyMatching + 174

2.3 启动 / 重启超时

App 因为启动 / 重启的工夫超过零碎容许的工夫限度,造成异样退出。

scene-create watchdog transgression: app exhausted real (wall clock) time allowance of 19.93 seconds, Elapsed total CPU time (seconds): 21.050 (user 21.050, system 0.000)

2.4 CPU 打爆

主线程死锁、启动 / 重启超时,都可能间接导致 CPU 打爆,造成异样退出。

三、Abort 问题的根因定位

Abort 问题经常没有显著线索进行问题定位,因而,解决难度比拟大。手淘曾经验过很屡次 Abort 问题数量飙升,但无从下手的事变,甚至还有一两次产生在双 11 前不久,但往往以“一群人苦逼的众测复现、复现之后也无奈确定是否真的复现”开场。

因而,咱们迫切需要基于已有教训,造成一套残缺的解决方案,疾速、精确地定位 / 解决问题。这就须要咱们从以下几个方面着手进行思考:
1.Abort 问题产生的场景:例如,哪个页面、什么操作。
2.Abort 问题产生的起因:例如,内存 Jetsam、主线程死锁、启动 / 重启超时、CPU 打爆。
3. 对于内存 Jetsam,需进一步定位到是否产生了内存泄露以及泄露的循环援用(Retain Cycle)。
4. 对于主线程死锁,需进一步定位到卡死的堆栈。
5. 对于启动 / 重启超时,以及 CPU 打爆,需进一步定位到堆栈。

接下来,咱们以手淘的主线程死锁问题为例,进行根因剖析。首先,来看一下某版本手淘 Abort 问题数据的总体视图:

因为 Abort 问题呈现之前,内存、CPU 使用量失常,因而初步判断造成异样退出的起因为主线程死锁。

查看相干日志文件,验证工夫、线索吻合,因而可最终确定造成异样退出的起因为主线程死锁。

四、Abort 问题的系统性解决方案

4.1 Abort 系统性解决方案难点:现场捕捉

为实现 Abort 问题的系统性解决方案,需充分考虑以下问题:
1. 通过 signal 9 杀死过程造成的 Abort 问题,往往难以通过信号量捕捉至堆栈。在这种状况下,应如何尽可能残缺地捕捉解体现场的要害信息?具体蕴含哪些信息?
2.App 解体时零碎处于极不稳固的状态,应如何保障解体现数据稳固落盘?
3. 在信息采集、数据捕捉的过程中,需对大量数据进行写入操作,应如何保障日志高性能写入?
4. 在数据量较大的状况下,数据的存储、上传可能对系统造成较大压力,应如何保证数据的高压缩率?

基于以上思考,咱们提出并设计了一套基于 mmap 的高性能、高压缩率、高一致性、可自解释的 trace 文件协定,作为 iOS 端高可用体系的数据载体。

4.1.1 mmap 数据存储层保证数据写入的高性能和高一致性

1. 通过 mmap 将一个文件或者其它对象映射到过程的地址空间,对内存的操作会由内核将数据写到对应的磁盘文件上;数据写入的性能与内存操作相当(略比内存操作高)
2. 用户过程解体之后,这块映射区仍由内核治理,能够保证数据的一致性

4.1.2 二进制编码协定保证数据压缩率最高

1. 具体编码协定
2. 实测编码在压缩率能达到 80% 以上,或者直观一点说,应用 50k 的内存能够记录下用户二十分钟内具体的应用记录,包含页面拜访记录、零碎事件、秒级别的内存、CPU 数据。

4.1.3 尽可能多的记录零碎多维度指标及异样事件

包含:
1. 性能数据,包含 CPU、内存数据,用于判断利用以后是不是解决 overload 状态
2. 大内存申请
3.Retain Cycle,用于定位 Jetsam Event
4. 卡顿,用于定位 watch dog kill
5. 以后存活 VC 实例数量

五、总结

在 App 的世界里,性能层面的差别曾经越来越难以体现。在这种状况下,良好的用户体验,往往是 App 致胜的要害。而 Abort 问题对于每一个 App 而言,都是对用户体验的最大挑战,须要 App 开发者给予足够的器重。
为了更好地发现解决解体问题,构建异样“感知 - 定位 - 复原”的运维能力闭环,晋升 App 应用体验,倡议接入阿里云解体剖析,反对各类异样事件采集,反对现场回溯剖析,帮忙您更好的进步 iOS App 稳定性。

钉钉搜寻 35248489,退出阿里云云原生利用研发平台 EMAS 技术交换群,探讨最新最热门的利用研发技术和实际。(或钉钉扫码退出)

作者:淘宝庐轩

退出移动版