
前言:本文旨在介紹iOS性能優(yōu)化中有關(guān)崩潰的介紹和采集。
一、APP常見的崩潰
- 野指針,即EXC_BAD_ACCESS
- 非法參數(shù)異常,比如參數(shù)傳為nil
- 數(shù)組越界
- 除0,即被除數(shù)為0
- 方法未找到,即unrecognized selector
- 舊設(shè)備老系統(tǒng)的崩潰
- OOM(低內(nèi)存崩潰)
- 崩潰到main函數(shù)
二、iOS中的崩潰類型
1、EXC_BAD_ACCESS
野指針引起的崩潰,訪問了一個已經(jīng)釋放的內(nèi)存而導(dǎo)致,向已經(jīng)釋放的對象或向它發(fā)送消息時,EXC_BAD_ACCESS就會出現(xiàn)。造成EXC_BAD_ACCESS最常見的原因是,在初始化方法中初始化變量時用錯了所有權(quán)修飾符,這會導(dǎo)致對象過早地被釋放。
2、SIGSEGV
段錯誤信息(SIGSEGV)是操作系統(tǒng)產(chǎn)生的一個更嚴(yán)重的問題。
3、SIGBUS
總線錯誤信號(SIGBUG)代表無效內(nèi)存訪問,即訪問的內(nèi)存是一個無效的內(nèi)存地址。也就是說,那個地址指向的位置根本不是物理內(nèi)存地址(它可能是某個硬件芯片的地址)。
4、SIGTRAP
SIGTRAP代表陷阱信號。它并不是一個真正的崩潰信號。它會在處理器執(zhí)行trap指令發(fā)送。LLDB調(diào)試器通常會處理此信號,并在指定的斷點處停止運行。如果你收到了原因不明的SIGTRAP,先清除上次的輸出,然后重新進(jìn)行構(gòu)建通常能解決這個問題。
5、EXC_ARITHETIC
當(dāng)要除零時,應(yīng)用會收到EXC_ARITHMETIC信號。這個錯誤應(yīng)該很容易解決。
6、SIGILL
SIGILL代表signal illegal instruction(非法指令信號)。當(dāng)在處理器上執(zhí)行非法指令時,它就會發(fā)生。執(zhí)行非法指令是指,將函數(shù)指針會給另外一個函數(shù)時,該函數(shù)指針由于某種原因是壞的,指向了一段已經(jīng)釋放的內(nèi)存或是一個數(shù)據(jù)段。
7、SIGABRT
SIGABRT代表SIGNAL ABORT(中止信號)。當(dāng)操作系統(tǒng)發(fā)現(xiàn)不安全的情況時,它能夠?qū)@種情況進(jìn)行更多的控制;必要的話,它能要求進(jìn)程進(jìn)行清理工作。
8、超時
這種崩潰通常比較容易分辨,因為錯誤碼是固定的0x8badf00d。在iOS上,它經(jīng)常出現(xiàn)在執(zhí)行一個同步網(wǎng)絡(luò)調(diào)用而阻塞主線程的情況。因此,永遠(yuǎn)不要進(jìn)行同步網(wǎng)絡(luò)調(diào)用。
9、一些被系統(tǒng)殺掉的情況,我們可以通過異常編碼來分析:
0x8badf00d,表示app在一段時間內(nèi)無響應(yīng)而被watchdog殺掉的情況
0xdeadfa11,表示app被用戶強(qiáng)制推出。
0xc00010ff,表示app因為運行造成設(shè)備溫度太高而被殺掉。
三、崩潰信息:
我們采集到的崩潰信息,主要包含的信息為:進(jìn)程信息、基本信息、異常、線程回溯。
- 進(jìn)程信息:崩潰進(jìn)程的相關(guān)信息,比如崩潰報告唯一標(biāo)識符、唯一鍵值、設(shè)備標(biāo)識。
- 基本信息:崩潰發(fā)生的日期、iOS版本。
- 異常信息:異常類型、異常編碼、異常的線程。
- 線程回溯:崩潰時的方法調(diào)用棧。
四、崩潰信息采集原理
1、捕獲異常
iOS中引發(fā)崩潰的代碼本質(zhì)上就兩類,一個是c++語言層面的錯誤,屬于比較底層的錯誤,比如野指針,除零,內(nèi)存訪問異常等等,這一類的錯誤可以通過信號機(jī)制來捕獲(signal或者是sigaction),即任何系統(tǒng)錯誤都會拋出一個錯誤信號,我們可以通過設(shè)定一個回調(diào)函數(shù),然后在回調(diào)函數(shù)里面進(jìn)行自己的處理;另一類是未捕獲異常(Uncaught Exception),iOS下面最常見的就是objective-c的NSException,比如,數(shù)組訪問元素越界。這些異常如果沒有在最上層try住,那么程序就崩潰了。
針對NSException的捕獲,通過調(diào)用NSSetUncaughtExceptionHandler來捕獲,系統(tǒng)錯誤通過注冊signal來捕獲,一般產(chǎn)生一個NSException的異常的時候,同時也會拋出一個signal的信號。
2、獲取堆棧信息
但捕獲到程序的異常后,我們需要解析app崩潰時的環(huán)境,即崩潰堆棧。
NSException的異常比較簡單,直接獲取崩潰name,reason和callstack;signal的堆棧的處理就比較麻煩些;另外,還就是需要分析APP的當(dāng)前線程信息以及所有的線程信息或者更加深一步的寄存器信息。不過這個時候拿到的堆棧是地址的形式,還需要第三步的符號表還原功能才能定位到代碼行號。
五、怎么排查崩到main函數(shù)的崩潰
設(shè)置異常崩潰-Xcode-Exception BreakPoint
-
設(shè)置-隱私-數(shù)據(jù)和分析-分析數(shù)據(jù),就可以導(dǎo)出崩潰日志
Xcode自帶Zombie檢測的開關(guān),選擇edit scheme - Run- Diagnostics - 勾選 Zombie Objects
Xcode-Window-Organizer-選擇你的APP-Crashes
Xcode自帶的bug分析工具--Crashes Origanizer,它可以報告在app上架期間的崩潰情況。
左側(cè)列出了App Store收集的崩潰列表,右側(cè)有詳細(xì)的崩潰原因。最有用的一個功能莫過于點擊"Open in Project"按鈕,它能直接將錯誤定位到當(dāng)前項目的崩潰點上。
六、深入了解iOS中的OOM(低內(nèi)存崩潰)
在iOS開發(fā)過程或者用戶反饋中,可能會經(jīng)??吹竭@樣的情況,用著用著就崩潰了,而在后臺查看崩潰棧的時候,找不到崩潰日志。其實這大多數(shù)的可能是系統(tǒng)產(chǎn)生了低內(nèi)存崩潰,也就是OOM(還有一種可能是主線程卡死,導(dǎo)致watchdog殺掉了應(yīng)用),而低內(nèi)存崩潰的日志,往往都是以JetsamEvent開頭的(下圖中展示),日志中有內(nèi)存頁大小(pageSize),CPU時間(cpuTime)等字段。
1、什么是OOM?
什么是OOM呢,它是out-of-memory的縮寫,字面意思就是內(nèi)存超過了限制。它是由于 iOS 的 Jetsam機(jī)制造成的一種“另類” Crash,它不同于常規(guī)的Crash,通過Signal捕獲等Crash監(jiān)控方案無法捕獲到OOM事件。
當(dāng)然還會有FOOM這樣的詞,代表的是Foreground-out-of-memory,是指App在前臺因消耗內(nèi)存過多引起系統(tǒng)強(qiáng)殺。這也就是本文要討論的。后臺出現(xiàn)OOM不一定都是app本身造成的,大多數(shù)是因為當(dāng)前在前臺的App占用內(nèi)存過大,系統(tǒng)為了保證前臺應(yīng)用正常運行,把后臺應(yīng)用清理掉了。
2、什么是Jetsam機(jī)制?
Jetsam機(jī)制可以理解為操作系統(tǒng)為了控制內(nèi)存資源過度使用而采用的一種管理機(jī)制。Jetsam是一個獨立運行的進(jìn)程,每一個進(jìn)程都有一個內(nèi)存閾值,一旦超過這個閾值Jetsam就會立刻殺掉這個進(jìn)程。
3、為什么要設(shè)計Jetsam機(jī)制?
首先設(shè)備的內(nèi)存是有限制的,并不是無限大的,所以內(nèi)存資源非常重要。系統(tǒng)進(jìn)程及用戶使用的其他app的進(jìn)程都會爭搶這個資源。由于iOS不支持交換空間,一旦觸發(fā)低內(nèi)存事件,Jetsam就會盡可能多的釋放應(yīng)用占用的內(nèi)存,這樣在iOS系統(tǒng)上出現(xiàn)系統(tǒng)內(nèi)存不足時,應(yīng)用就會被系統(tǒng)終止。
從設(shè)置-隱私-數(shù)據(jù)和分析-分析數(shù)據(jù)中導(dǎo)出的數(shù)據(jù)可以看出:以JetsamEvent開頭的則為OOM的信息

4、檢測OOM工具:
- 騰訊 OOMDetector
- 微信 matrix
