一、背景

崩溃(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技术交流群,探讨最新最热门的应用研发技术和实践。(或钉钉扫码加入)

作者:淘宝庐轩

iOS Abort问题系统性解决方案的更多相关文章

  1. iOS关于模块化开发解决方案(纯干货)

    关于iOS模块化开发解决方案网上也有一些介绍,但真正落实在在具体的实例却很少看到,计划编写系统文章来介绍关于我对模块化解决方案的理解,里面会有包含到一些关于解耦.路由.封装.私有Pod管理等内容:并编 ...

  2. iOS 常见 Crash 及解决方案

    一.访问了一个已经被释放的对象 在不使用 ARC 的时候,内存要自己管理,这时重复或过早释放都有可能导致 Crash. 例子 NSObject * aObj = [[NSObject alloc] i ...

  3. iOS全埋点解决方案-UITableView和UICollectionView点击事件

    前言 在 $AppClick 事件采集中,还有两个比较特殊的控件: UITableView •UICollectionView 这两个控件的点击事件,一般指的是点击 UITableViewCell 和 ...

  4. iOS 横竖屏切换解决方案

    iOS要实现横竖屏切换很简单,不需要使用任何第三方,只需要实现几个方法就可以了. 1.设置系统支持横竖屏[General]->[Targets]-> [Deployment info]-& ...

  5. iOS objc_msgSend 报错解决方案

    错误代码: objc_msgSend(self.beginRefreshingTaget, self.beginRefreshingAction, self); Too many arguments ...

  6. JavaScript调用App原生代码(iOS、Android)通用解决方案

    实际场景 场景:现在有一个H5活动页面,上面有一个登陆按钮,要求点击登陆按钮以后,唤出App内部的登录界面,当登录成功以后将用户的手机号返回给H5页面,显示出来.这个场景应该算是比较完整的一次H5中的 ...

  7. Missing iOS Distribution signing identity解决方案

    相信很多朋友跟我遇到相同的问题,之前iOS发布打包的证书没问题,现在莫名其妙的总是打包失败,并且报如下错误 第一反应,是不是证书被别人搞乱了.于是去Developer Member Center,把所 ...

  8. 在项目中全局添加FastClick导致图片上传插件在ios端失效的解决方案

    ---恢复内容开始--- 项目是移动端的项目,为了解决300ms的click延迟,所以在全局中加入了FastClick,引入的方式很简单,网上一大堆教程,这里不做赘述 我们就谈,我遇到的问题: 某天产 ...

  9. 键盘优雅弹出与ios光标乱飘解决方案

    前言 在移动开发中,会遇到这样的情况,比如说有一个输入框在最底部的时候,我们弹起输入框,输入框不会在输入键盘上. 说明白简单点就是,输入框被键盘挡住了.而且在原生中,输入框应该正好在输入键盘上,但是h ...

随机推荐

  1. Ubuntu构建Docker私有仓库(Repository) 配置过程笔记

    一.准备: 1.服务器(或者虚拟机2台,我的服务环境[  阿里云服务器-Ubuntu 1804 +百度云-Ubuntu 1604]) 2.有效镜像(我这里以上一篇随笔镜像作为有效镜像https://w ...

  2. k8s(00)入门知识介绍

    系列文章说明 本系列文章,可以基本算是 老男孩2019年王硕的K8S周末班课程 笔记,根据视频来看本笔记最好,否则有些地方会看不明白 需要视频可以联系我 k8s概念入门 目录 系列文章说明 k8s概念 ...

  3. [redis] -- 集群篇

    三种集群方式 主从同步:主从复制模式中包含一个主数据库实例(master)与一个或多个从数据库实例(slave) 优点: master能自动将数据同步到slave,可以进行读写分离,分担master的 ...

  4. [并发编程] -- ThreadPoolExecutor篇

    Executor框架 Executor框架的两级调度模型(基于HotSpot) 在上层,Java多线程程序通常把应用分解为若干个任务,然后使用用户级的调度器(Executor框架)将这些任务映射为固定 ...

  5. 题解 SP1841 【PPATH - Prime Path】

    模拟赛考到了这个题,但我傻傻的用了\(DFS\),于是爆了零 后来才想明白,因为搜索树的分支很多,但答案的深度却又没有那么深,所以在这里\(BFS\),而\(DFS\)一路搜到底的做法则会稳稳地\(T ...

  6. 如何在Python对Excel进行读取

    在python自动化中,经常会遇到对数据文件的操作,比如添加多名员工,但是直接将员工数据写在python文件中,不但工作量大,要是以后再次遇到类似批量数据操作还会写在python文件中吗? 应对这一问 ...

  7. 附002.Nginx全系列大总结

    Nginx全系列总结如下,后期不定期更新. 欢迎基于学习.交流目的的转载和分享,禁止任何商业盗用,同时希望能带上原文出处,尊重ITer的成果,也是尊重知识. 若发现任何错误或纰漏,留言反馈或右侧添加本 ...

  8. 面试题三十:包含min函数的栈

    定义一个栈的数据结构,请实现一个每次都能找到栈中的最小元素,要求时间复杂度O(1).意思就是说每次进栈出栈后,min函数总能在时间1的前提下找到.方法一:由于每次循序遍历栈的话时间复杂度为n,所以要想 ...

  9. 微服务迁移记(五):WEB层搭建(2)-SpringSecurity集成

    一.redis搭建 二.WEB层主要依赖包 三.FeignClient通用接口 以上三项,参考<微服务迁移记(五):WEB层搭建(1)> 接下来,集成SpringSecruity,实现用户 ...

  10. Java Servlet详解(体系结构+注解配置+生命周期)

    Java Servlet详解(注解配置+生命周期) 什么是Servlet : (Server applet)? 顾名思义:服务端的小程序 Servlet只是一个接口,定义了Java被浏览器访问到(To ...