
iOS 崩潰原因
- CPU resource Limit CPU 占用率過高
- 觸發(fā)watch dog 主線程超過20s未響應(yīng)
- Memory limit exceeded內(nèi)存超出上限
- Memory pressure exit : JatSam
- background task timeout
崩潰原因解析
CPU resource limit
應(yīng)用程序處理任務(wù)時間過長,系統(tǒng)可能會終止程序執(zhí)行,建議使用 iOS 13BGProcessingTaskwatch dog
出現(xiàn)場景: 應(yīng)用啟動或者重新進入前臺
可能原因:
- dead lock
- 無限循環(huán)
- 主線程無法完成的任務(wù)
注意:處于調(diào)試下是不會出現(xiàn) Watchdog 的
- Memory limit exceeded
- JatSam
在 Linux 系統(tǒng)中,交換空間(swap space)可以用來存放內(nèi)存中不常用的臨時數(shù)據(jù)。而 iOS 系統(tǒng)因為閃存容量和讀寫壽命的原因,并沒有引入交換空間,取而代之的是 Compressed memory 技術(shù),即當內(nèi)存緊張時,壓縮一些內(nèi)存內(nèi)容,并在需要時解壓。但副作用是會造成較高的 CPU 占用甚至卡頓,手機耗電量也會隨之增加。
為了解決上面的問題,蘋果設(shè)計了 Jetsam 機制。其工作方式是當內(nèi)存不足時,系統(tǒng)會通知前臺應(yīng)用去釋放內(nèi)存(通過 applicationDidReceiveMemoryWarning 方法和 UIApplicationDidReceiveMemoryWarningNotification 通知),如果內(nèi)存壓力依然存在,將會終止一些后臺應(yīng)用。最終內(nèi)存還不夠的話,就會終止當前應(yīng)用(FOOM),并且上報日志。
iOS dsym和link-map文件分析
/Users/sunxx/Library/Developer/Xcode/Archives/2020-10-20/xxxPM.xcarchive/dSYMs/AFNetworking.framework.dSYM/Contents/Resources/DWARF/AFNetworking
使用atos解析crash 文件
atos -arch arm64 -o /Users/sun/Library/Developer/Xcode/Archives/xx.xcarchive/dSYMs/AFNetworking.framework.dSYM/Contents/Resources/DWARF/AFNetworking -l 0x1000e4000 0x00000001000effdc
-[xxxxviewController runContenxt:]
- -arch:指令集,現(xiàn)在release的都包含arm64指令集架構(gòu)。可以通過
atool -hv appname查看相關(guān)的指令集; - -o :目標文件:可執(zhí)行文件。
- -l : load address,及發(fā)生crash對應(yīng)的鏡像起始地址;后面一個地址是symblicate address,及符號地址。
使用linkmap 查看二進制文件信息
理解Link Map File,可以幫助我們:
- 理解鏈接過程
- 理解Crash時,通過Symbols還原出源碼的機制
- 理解內(nèi)存分段、分區(qū)
- 分析可執(zhí)行文件中哪個類或庫占用比較大,進行安裝包瘦身
生成link-map 文件
Xcode 開啟編譯選項:設(shè)置 Write Link Map file 為 YES

查看路徑:~/Library/Developer/Xcode/DerivedData/XXX-
- __text表示編譯后的程序執(zhí)行語句
- __data表示已初始化的全局變量和局部靜態(tài)變量
- __bss表示未初始化的全局變量和局部靜態(tài)變量
- __cstring表示代碼里的字符串常量
其中一些常用的信息如下:
// __TEXT段中的一些Section
1. __text: 代碼節(jié),存放機器編譯后的代碼
2. __stubs: 用于輔助做動態(tài)鏈接代碼(dyld).
3. __stub_helper:用于輔助做動態(tài)鏈接(dyld).
4. __objc_methname:objc的方法名稱
5. __cstring:代碼運行中包含的字符串常量,比如代碼中定義`#define kGeTuiPushAESKey @"DWE2#@e2!"`,那DWE2#@e2!會存在這個區(qū)里。
6. __objc_classname:objc類名
7. __objc_methtype:objc方法類型
8. __ustring:
9. __gcc_except_tab:
10. __const:存儲const修飾的常量
11. __dof_RACSignal:
12. __dof_RACCompou:
13. __unwind_info:
// __DATA段中的一些Section
1. __got:存儲引用符號的實際地址,類似于動態(tài)符號表
2. __la_symbol_ptr:lazy symbol pointers。懶加載的函數(shù)指針地址。和__stubs和stub_helper配合使用。具體原理暫留。
3. __mod_init_func:模塊初始化的方法。
4. __const:存儲constant常量的數(shù)據(jù)。比如使用extern導(dǎo)出的const修飾的常量。
5. __cfstring:使用Core Foundation字符串
6. __objc_classlist:objc類列表,保存類信息,映射了__objc_data的地址
7. __objc_nlclslist:Objective-C 的 +load 函數(shù)列表,比 __mod_init_func 更早執(zhí)行。
8. __objc_catlist: categories
9. __objc_nlcatlist:Objective-C 的categories的 +load函數(shù)列表。
10. __objc_protolist:objc協(xié)議列表
11. __objc_imageinfo:objc鏡像信息
12. __objc_const:objc常量。保存objc_classdata結(jié)構(gòu)體數(shù)據(jù)。用于映射類相關(guān)數(shù)據(jù)的地址,比如類名,方法名等。
13. __objc_selrefs:引用到的objc方法
14. __objc_protorefs:引用到的objc協(xié)議
15. __objc_classrefs:引用到的objc類
16. __objc_superrefs:objc超類引用
17. __objc_ivar:objc ivar指針,存儲屬性。
18. __objc_data:objc的數(shù)據(jù)。用于保存類需要的數(shù)據(jù)。最主要的內(nèi)容是映射__objc_const地址,用于找到類的相關(guān)數(shù)據(jù)。
19. __data:暫時沒理解,從日志看存放了協(xié)議和一些固定了地址(已經(jīng)初始化)的靜態(tài)量。
20. __bss:存儲未初始化的靜態(tài)量。比如:`static NSThread *_networkRequestThread = nil;`其中這里面的size表示應(yīng)用運行占用的內(nèi)存,不是實際的占用空間。所以計算大小的時候應(yīng)該去掉這部分數(shù)據(jù)。
21. __common:存儲導(dǎo)出的全局的數(shù)據(jù)。類似于static,但是沒有用static修飾。比如KSCrash里面`NSDictionary* g_registerOrders;`, g_registerOrders就存儲在__common里面
0x101AE43A0 0x00000018 [699] __OBJC_$_PROTOCOL_REFS_PLPlayerDelegate
0x101AE43B8 0x00000128 [699] __OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_PLPlayerDelegate
0x101AE44E0 0x00000060 [699] __OBJC_$_PROTOCOL_METHOD_TYPES_PLPlayerDelegate
再看一段編譯的代碼
0x10181373D 0x0000008D [699] literal string: ^{AudioBufferList=I[1{AudioBuffer=II^v}]}88@0:8@16^{AudioBufferList=I[1{AudioBuffer=II^v}]}24{AudioStreamBasicDescription=dIIIIIIII}32q72q80
0x1018137CA 0x00000011 [699] literal string: v32@0:8@16i24i28
0x1018137DB 0x00000015 [699] literal string: v24@0:8@"PLPlayer"16
0x1018137F0 0x00000018 [699] literal string: v32@0:8@"PLPlayer"16q24
0x101813808 0x00000021 [699] literal string: v32@0:8@"PLPlayer"16@"NSError"24
0x101813829 0x0000001F [699] literal string: v48@0:8@"PLPlayer"16{?=qiIq}24
0x101813848 0x0000002E [699] literal string: v48@0:8@"PLPlayer"16^{__CVBuffer=}24q32i40i44
0x101813876 0x00000097 [699] literal string: ^{AudioBufferList=I[1{AudioBuffer=II^v}]}88@0:8@"PLPlayer"16^{AudioBufferList=I[1{AudioBuffer=II^v}]}24{AudioStreamBasicDescription=dIIIIIIII}32q72q80
0x10181390D 0x00000020 [699] literal string: v32@0:8@"PLPlayer"16@"NSData"24
0x10181392D 0x00000023 [699] literal string: v40@0:8@"PLPlayer"16@"NSData"24q32
0x101813950 0x0000001B [699] literal string: v32@0:8@"PLPlayer"16i24i28
0x10181396B 0x00000018 [699] literal string: v28@0:8@"PLPlayer"16B24
0x101813983 0x00000011 [699] literal string: v36@0:8@16i24@28
0x101813994 0x0000001C [699] literal string: v24@0:8@"SuperPlayerView"16
0x1018139B0 0x0000002C [699] literal string: v36@0:8@"SuperPlayerView"16i24@"NSString"28
0x1018139DC 0x0000000C [699] literal string: @"PLPlayer"
由此可以推斷,Xcode 在鏈接動態(tài)庫的時候只是在使用動態(tài)庫文件的位置添加了一個描述符而已
lipo 使用
- 查看庫信息
lipo -info xxx.framework/xxx - 合并靜態(tài)庫
lipo -create xxx1.a xxx2.a -output xxxnew
lipo -create xxx1.framework/xxx1 xxx2.framework/xxx2 -output xxxnew.framework/xxxnew
- 移除指定架構(gòu)
lipo 靜態(tài)庫路徑 -remove 架構(gòu)名 -output 存放路徑
- 靜態(tài)庫拆分
lipo 靜態(tài)庫源路徑 -thin 架構(gòu)名 -output 存放路徑