對於一個可執行文件來講,它的加載過程是: 分爲兩大部分:緩存
操做系統加載可執行文件,經過fork(建立一個進程)指令在新的空間內來執行可執行文件,加載依賴的可執行文件(mach-o)文件,定位其內部與外部指針引用,例如字符串與函數,執行聲明爲attribute((constructor))的C函數,加載擴展(Category)中的方法,C++靜態對象加載,調用ObjC的+load函數bash
基本流程:多線程
App 開始啓動後,系統首先加載可執行文件(自身 App 的全部 .o 文件的集合),而後加載動態連接器 dyld,dyld 是一個專門用來加載動態連接庫的庫。 執行從 dyld 開始,dyld 從可執行文件的依賴開始,遞歸加載全部的依賴動態連接庫。 動態連接庫包括:iOS 中用到的全部系統 framework,加載 OC runtime 方法的 libobjc,系統級別的 libSystem,例如 libdispatch(GCD) 和 libsystem_blocks (Block)。app
動態連接庫的加載過程主要由dyld來完成,dyld是蘋果的動態連接器。框架
官方文檔: developer.apple.com/library/arc…異步
Mach-O是OS X中二進制文件的本機可執行格式,是傳送代碼的首選格式。可執行格式肯定二進制文件中的代碼和數據被讀入內存的順序。代碼和數據的排序會影響內存使用和分頁活動,從而直接影響程序的性能。段的大小經過其包含的全部段中的字節數來度量,並向上舍入到下一個虛擬內存頁邊界。 Mach-O二進制文件被組織成segements。每一個segement包含一個或多個部分。每一個部分都有不一樣類型的代碼或數據。segement始終從頁面邊界開始,但section不必定是頁面對齊的。所以,segement終是4096字節或4千字節的倍數,其中4096字節是最小大小。 Mach-O可執行文件的segement和section根據其預期用途命名。segement名稱的約定是使用以雙下劃線開頭的全大寫字母(例如,TEXT); section名稱的約定是使用以雙下劃線開頭的全小寫字母(例如, text)。 Mach-O可執行文件中有幾個可能的segements,但只有兩個與性能有關:__TEXT段和__DATA段。函數
The __TEXT Segment: Read Only __TEXT segment是包含可執行代碼和常量數據的只讀區域。按照慣例,編譯器工具建立具備至少一個只讀__TEXT segment的每一個可執行文件。因爲該段是隻讀的,所以內核能夠將__TEXT segment直接從可執行文件映射到內存中一次。當segment被映射到內存時,它能夠在全部進程之間共享其內容。 (這主要是框架和其餘共享庫的狀況。)只讀屬性還意味着構成__TEXT segment的頁面永遠沒必要保存到後備存儲。若是內核須要釋放物理內存,它能夠丟棄一個或多個__TEXT頁面,並在須要時從磁盤從新讀取它們。 __TEXT segment的主要部分,sections分佈工具
The __DATA Segment: Read/Write __DATA segment 包含可執行文件的很是量變量。該 segement 是可讀寫的,由於它是可寫的,因此對於與庫連接的每一個進程,邏輯上覆制靜態庫或其餘動態共享庫的__DATA段。當內存頁面可讀寫時,內核會使其變爲copy-on-write。此技術能夠作到,動態庫是在內存中共享的,能夠被其餘各個進程訪問,但由於__DATA Segment是可讀可寫的,就會經過某一進程對共享的_DATA Segment有寫操做的時候,再進行單獨的_DATA內存空間複製。 __DATA segment 有許多部分,其中一些僅由動態連接器使用。下面 列出了能夠出如今__DATA segment 中的一些更重要的部分。有關段的完整列表,請參閱Mach-O運行時體系結構。佈局
Mach-O 性能影響 Mach-O可執行文件的__TEXT segment和__DATA segment的組成與性能有直接關係。優化這些sections的技術和目的是不一樣的。可是,它們的共同目標是:提升內存使用效率。性能
最典型的Mach-O的文件由可執行代碼組成,在__TEXT,__text當中。如__TEXT segment,該__TEXT是隻讀的,並直接映射到可執行文件,因此若是內核須要回收某些__text頁面佔用的物理內存,就沒必要將頁面保存到back store再將其分頁。它只須要釋放內存,並在後面代碼引用的時候從磁盤從新讀回。雖然這比交換內存分頁的成本低,由於這只是一個磁盤訪問,而不是兩個內存分頁的交換 , 但這仍然很損耗性能,特別是若是必須從磁盤從新建立許多頁面。
對於這種狀況的改進,是經過程序從新排序來改進代碼的引用位置,如改進參考位置中所述。該技術將方法和功能組合在一塊兒,具體取決於它們的執行順序,調用頻率以及它們相互調用的頻率。若是__text部分組中的頁面以這種方式邏輯上起做用,則它們不太可能被屢次釋放和讀回。例如,若是將全部啓動時初始化函數放在一個或兩個頁面上,則在發生初始化後沒必要從新建立頁面。
與__TEXT段不一樣,__DATA能夠寫入段,所以段中的頁面__DATA不可共享。框架中的很是量全局變量可能會對性能產生影響,由於與框架連接的每一個進程都會得到這些變量的副本。解決這個問題的主要解決辦法是儘量多的非恆定的全局變量儘量轉移到__TEXT,__const經過宣佈他們部分const。減小共享內存頁面描述了此技術和相關技術。這一般不是應用程序的問題,由於應用程序中的__DATA部分不與其餘應用程序共享。
編譯器將不一樣類型的很是量全局數據存儲在段的不一樣部分中__DATA。這些類型的數據是未初始化的靜態數據和符號與未聲明的「暫定定義」的ANSI C概念一致extern。未初始化的靜態數據位於__bss段的__DATA部分中。暫定的符號在__common 該__DATA部分。
該 ANSI C和 C ++標準指定系統必須將未初始化的靜態變量設置爲零。(未初始化的其餘類型的未初始化數據。)因爲未初始化的靜態變量和臨時定義符號存儲在單獨的部分中,所以系統須要以不一樣方式對待它們。可是當變量位於不一樣的部分時,它們更有可能最終出如今不一樣的內存頁面上,所以能夠單獨進行交換,從而使代碼運行速度變慢。如減小共享內存頁面中所述,這些問題的解決方案是在段的一個部分中合併不是常量全局數據__DATA。
dyld的加載過程會初始化Runtime系統,在此階段,有至關多的優化工做能夠作
這過程包括:在Runtime系統加載之後,開始進行初始化
從上面能夠得出如下幾個結論,影響該階段啓動時間的因素以下:
static int x;
static short conv_table [128];
//更換爲
static int x = 0;
static short conv_table [128] = {0};
複製代碼
減小靜態變量的使用 3. 減小符號表的導出 經過設置-exported_symbols_list或-unexported_symbols_lis來限制符號表的導出,從而減小dyld的工做量 4. 去除沒有使用的動態庫依賴,明確所依賴的frameworks是require仍是optional,optional會動態進行額外檢查 5. 刪除沒有用的方法 6. 減小+load函數的實現,並減小在其中操做的邏輯 7. 對某些常常調用的代碼進行二進制化,生成靜態庫,多使用靜態庫代替動態庫,將多個靜態庫框架,集中製做成靜態framework,從而可以減小dyld的連接工做 關於冷啓動和熱啓動的不一樣以下: