數控面板解鎖 解密
數控面板解鎖 解密APP啟動最佳化大致分為
Main函式之前
和
Main函式
之後,Main函式之後的最佳化大多是使用
懶載入
的方式或者最佳化
業務載入順序
,下面重點看一下
Main函式之前
的最佳化。所謂冷啟動最佳化就是應用第一次載入進記憶體的最佳化。
Pre-main時間
Main函式之前都幹了什麼?
Xcode
新增環境變數
DYLD_PRINT_STATISTICS
,日誌看一下
pre-main
啟動時間,此處demo以本人實際專案為例。
Xcode
執行專案,日誌輸出如下:
**Total pre-main time: 774。24 milliseconds (100。0%)** **dylib loading time: 138。83 milliseconds (17。9%)** **rebase/binding time: 5。88 milliseconds (0。7%)** **ObjC setup time: 83。71 milliseconds (10。8%)** **initializer time: 545。80 milliseconds (70。4%)** **slowest intializers :** **libSystem。B。dylib : 9。07 milliseconds (1。1%)** **libMainThreadChecker。dylib : 50。17 milliseconds (6。4%)** **libglInterpose。dylib : 185。33 milliseconds (23。9%)** **推送助手 : 525。02 milliseconds (67。8%)** 複製程式碼
可以看到
pre-main
也就是main函式之前總耗時
774。24
毫秒
dylib loading
:
動態庫的載入
。儘量減少自定義動態庫,蘋果建議大於
6個
的話儘可能合併。
rebase/binding
:重定位/繫結。macho中的記憶體地址是偏移地址,需要加上首地址
ASLR
變成真實的虛擬記憶體地址,這就是
重定位
。區是於連結,連結是編譯時,繫結是執行時,繫結是繫結的共享快取的地址,外部符號越多繫結時間相對越長。
ObjC setup
:
OC類的註冊
。要儘可能的刪除沒有用到的類,雖然沒有用到,但是隻要存在,就會對類進行處理。
initializer time
:
load()以及建構函式
。load()
別做耗時
的事情,這是啟動時間最佳化的重點,一般是
最佳化業務邏輯
。
slowest intializers
詳細列出了
initializer time
中具體耗時情況,我的專案中耗時最多的是主工程的載入,耗時
525。02
毫秒
對於
Main函式之前
的最佳化,先拋開主工程的載入,最佳化建議如下:
減少
自定義動態庫,控制在
6
個以內
刪除
沒有用到的類
load()別做耗時
的事情。
虛擬記憶體
最佳化主工程的載入之前,先了解一下什麼是
虛擬記憶體
,簡單總結如下。
一開始程式載入的記憶體是
物理記憶體
,但是軟體發展速度太快了導致物理記憶體不夠用,畢竟一個程式就佔用一塊物理記憶體,而且直接使用物理記憶體存在
安全隱患
,駭客很容易攻擊這塊記憶體。
為了讓記憶體安全以及記憶體夠用就產生了
虛擬記憶體
,現在我們所說的記憶體都是虛擬記憶體,
CPU
上的一個模組會把
虛擬記憶體
翻譯成
物理記憶體
,這樣虛擬記憶體和物理記憶體就有一個
對映表
,為了高效的地址翻譯,不可能一個位元組一個位元組的翻譯,於是出現了
記憶體分頁管理
,即以頁為單位翻譯,
iphone6s
之後一頁是
16
位元組,
iphon6s之前
一頁是
4
位元組,所以iphone6s之後啟動時間會比較快,因為記憶體翻譯很快。虛擬記憶體地址很大有4G,這樣就解決了記憶體不夠用的情況。
程式載入
不會把虛擬記憶體
根據對映表都載入進
物理記憶體
中,而是
用到哪一頁或哪幾頁就把用到的載入進去
。比如當程式訪問虛擬記憶體
p1
頁時,如果物理記憶體中沒有,就會發生
缺頁中斷
,這時就要把這塊虛擬記憶體載入進物理記憶體,ios程式的
冷啟動
是發生
缺頁中斷
最多的地方,因為這時的物理記憶體中沒有對應的虛擬記憶體,雖然載入進物理記憶體很快,但是如果有非常多的缺頁中斷,那麼還是會影響
效率
的。
總結:
過多的缺頁中斷
就會影響APP啟動速度,所以我們要儘可能的
減少缺頁中斷
。比如啟動APP時必須呼叫10個方法,但是這10個方法分佈在10個不同的頁中,那麼就會發生10次缺頁中斷,如果把啟動時必須呼叫的這10個方法放在同一個或者一兩個頁中,那麼就會
減少缺頁中斷的次數
。下面的重點就是如何
重排二進位制
減少缺頁中斷的次數。
二進位制重排前
Xcode
,
command+control+i
啟動
instruments
選中
System Trace
,點選啟動,當APP啟動後進入第一個頁面,點選暫停,這時就會記錄從啟動到第一個頁面的過程資料。搜尋
Main thread
檢視虛擬記憶體中
缺頁中斷情況
,如下圖。
分析:缺頁中斷有
384
次,耗時
58。69
毫秒,啟動耗時
62。99
毫秒,缺頁中斷耗時佔了大部分。最佳化缺頁中斷次數的前提是有沒有浪費,有浪費的缺頁中斷才需要最佳化。
啟動時函式的載入順序
我們上面說為了減少缺頁中斷,需要儘可能的把啟動時函式的呼叫放在同一個頁中,下面看下整個應用裡函式載入進記憶體中的順序。
Xcode->build setting->Link Map->Write Link Map File->YES
,編譯後在
bulid
中檢視這個
link map
檔案如下:
link map
中這些函式呼叫是根據
編譯順序排列
的,我們想要減少缺頁中斷,就要儘可能的把啟動時的函式呼叫
放在最前面
。可能你會覺得在Xcode中
重新排列
檔案的編譯順序就可以,但是我們是要把啟動時呼叫的
函式
放在最前面,而不是啟動時的檔案放在最前面,畢竟檔案中的函式有很多,但是隻有個別函式在啟動時會呼叫。
如何重排二進位制
那麼我們如何找出APP啟動時呼叫了哪些函式呢?
Clang
的插樁法可以很好的解決這個問題。根據Clang官方文件文件步驟如下:
1。
Xcode->build setting->Other C Flags->-fsanitize-coverage=func,trace-pc-guard
編寫程式生成。order檔案,比如gy。order,clang會根據此檔案對link map檔案中的函式重新排列,程式中需要實現的關鍵函式如下:
__sanitizer_cov_trace_pc_guard_init
:app中有多少個函式就會有多少個符號
__sanitizer_cov_trace_pc_guard
:hook所有回撥函式,
APP啟動時
呼叫了多少個函式,就會呼叫多少次該函式。clang在編譯時會把該函式插入到函式的開始位置。
具體demo如下:
#import
demo分析:
__sanitizer_cov_trace_pc_guard
,hook函式是多執行緒的,所以為了執行緒安全,需要定義一個
原子佇列
存放。
結構體入棧函式
OSAtomicEnqueue
中
offsetof(SYNode, next)
目的是指定存放下一個函式的位置。
執行專案點選螢幕,複製沙盒中生成的
gy。order
檔案放在專案根目錄下,
xcode->order File->。/gy。order
,同時需要刪除
Xcode->build setting->Other C Flags->-fsanitize-coverage=func,trace-pc-guard
配置。clang會根據gy。order重新排序函式編譯的順序,再來看一下link map是否重排成功了。
很明顯
link map
確實改變了,我們再
System Trace
看一下主線中缺頁中斷是否優化了。
對比一下沒排序前,缺頁中斷有
384
次,耗時58。69毫秒,啟動耗時62。99毫秒。現在缺頁中斷
271
,耗時40。03,啟動耗時44。58,啟動大概優化了不到20毫秒。目前測試的專案不是很大,如果專案很大的話,比如微信,那麼這個啟動最佳化還是很可觀的。
上一篇:魚池防水的塗料作用