導(dǎo)讀
最近筆者所在公司的iOS App端在經(jīng)歷了幾個版本的瘋狂的業(yè)務(wù)迭代,幾個主要模塊例如首頁喇喉、分類在最近的幾次迭代中變得越來越重祖今,最終在最近的新版本暴露出了性能問題。
- AdHoc包中設(shè)置的內(nèi)存報(bào)警最近經(jīng)常被觸發(fā)閥值
- 線上的監(jiān)控日志的性能相關(guān)的crash明顯增加
由于不排除之后的版本這幾個一級模塊的業(yè)務(wù)會變的越來越重拣技,也為了技術(shù)進(jìn)階千诬,筆者開始著手于本次內(nèi)存相關(guān)的deep learning和內(nèi)存優(yōu)化工作。
專題簡述
關(guān)于內(nèi)存優(yōu)化膏斤,筆者希望從下面三個方面進(jìn)行整理和論述
1徐绑、原理篇:OS&iOS的內(nèi)存管理機(jī)制
2、調(diào)試篇:調(diào)試工具的使用(Allocations&VMTracker&Activity Monitor) 和 監(jiān)控工具的實(shí)現(xiàn)原理莫辨、開發(fā)和部署線上監(jiān)控
3傲茄、實(shí)戰(zhàn)篇:實(shí)戰(zhàn)優(yōu)化App,減小內(nèi)存峰值衔掸,消除內(nèi)存泄漏和危險(xiǎn)引用(野指針)
OS/iOS內(nèi)存管理機(jī)制
筆者希望帶著解決問題去思考和學(xué)習(xí)烫幕,關(guān)于內(nèi)存管理機(jī)制的學(xué)習(xí)俺抽,筆者參考的是
2016年QQ瀏覽器的莊延軍在SegmentFault的一次技術(shù)分享
問題引子
1敞映、在桌面系統(tǒng)中很少會有應(yīng)用會因?yàn)槭褂脙?nèi)存過多被Kill掉,為啥iOS會呢磷斧?
2振愿、虛擬內(nèi)存(VM)是什么捷犹?為啥有時它會超過物理總內(nèi)存呢?虛擬內(nèi)存過高會引起內(nèi)存警告嗎冕末?
3萍歉、Allocations中的Dirty Size和Resident Size分別指的是什么?(Swapped Size)All Heap & Anonymous VM是什么档桃?
4枪孩、iOS的內(nèi)存管理機(jī)制是什么樣的?他是基于什么原則來Kill掉進(jìn)程的藻肄?
5蔑舞、內(nèi)存有分類嗎?什么類型的內(nèi)存可以回收嘹屯?
6攻询、App的優(yōu)化,什么地方占用內(nèi)存多州弟,什么地方可以優(yōu)化钧栖?如何避免內(nèi)存峰值過高?
自己的拓展思考
1婆翔、mach-o 文件是我們開發(fā)的App的靜態(tài)展開方式拯杠,當(dāng)App在運(yùn)行的時候被加載到進(jìn)程中,mach-o文件內(nèi)容怎么被加載到虛擬內(nèi)存啃奴?VM又是怎么以什么樣的構(gòu)建的阴挣,是一種類類似于表的數(shù)據(jù)結(jié)構(gòu)的承載嗎?
2纺腊、cpu畔咧、寄存器、總線揖膜、內(nèi)存的工作原理誓沸?
一、基本的概念和原理
- 虛擬內(nèi)存機(jī)制(VM)
二壹粟、iOS的內(nèi)存管理
-
iOS運(yùn)行時程序進(jìn)程的分布
- 進(jìn)程加載器會把mach-o文件加載到內(nèi)存中(VM)拜隧。(會有一部分需要執(zhí)行VM映射到物理內(nèi)存上or將代碼、數(shù)據(jù)段全部加載到物理內(nèi)存上呢趁仙?)
- mach-o文件是有個頭洪添,里面記錄了所有段的信息(VM偏移,VM地址雀费,大懈缮荨), 進(jìn)程加載器根據(jù)這些頭各個段有些加載到物理內(nèi)存中(代碼段和數(shù)據(jù)段(全局靜態(tài)區(qū):初始化全局變量&static變量, BSS段:未初始化))盏袄,有的在可執(zhí)行文件內(nèi)只是個占位符忿峻,實(shí)際加載到虛擬內(nèi)存的時候才會分配內(nèi)存薄啥。
- heap向高位地址擴(kuò)散,stack向地位地址擴(kuò)散逛尚,保證兩者之間有足夠的VM分配垄惧。
iOS中的內(nèi)存段分類
-
iOS內(nèi)存管理
- iOS采用了全功能的內(nèi)存管理方式,有段式和頁式
- 上圖顯示的是我們需要管理的Dirty Memory的內(nèi)存管理的原理
- 在heap上創(chuàng)建的對象
- decompressed imgaes绰寞,一是從閃存中加載到內(nèi)存的Images:通過imageName:方法加載的Asserts內(nèi)的圖片文件(大)到逊, 二是SDWebImage加載的網(wǎng)絡(luò)圖片在內(nèi)存和磁盤中都持有一份緩存,為了性能在內(nèi)存中持有了
- caches
- App層用Region對象來管理相應(yīng)的VM
- 在kernal層用VM Object對象來管理虛擬內(nèi)存和真實(shí)物理內(nèi)存的map
注意
在這里就可以回答引子里面提出的第一個問題:
在桌面系統(tǒng)中很少會有應(yīng)用會因?yàn)槭褂脙?nèi)存過多被Kill掉滤钱,為啥iOS會呢蕾管?
- iOS系統(tǒng)中沒有swap機(jī)制:(1)閃存的容量有限 (2)閃存的讀寫次數(shù)有限,頻繁讀寫會會降低壽命
思考
代碼是要加載到內(nèi)存中的菩暗,iOS沒有swap機(jī)制代碼很大的app不是很占內(nèi)存嗎掰曾?
不是:代碼段是Clean Memory,在閃存中有備份停团,在內(nèi)存緊張的時候會被OS回收旷坦,后面再使用的話可以從閃存中重新構(gòu)建。
-
iOS低內(nèi)存處理機(jī)制jetsam
OS維護(hù)了一個優(yōu)先級隊(duì)列佑稠,從上往下秒梅,優(yōu)先級遞增
- 當(dāng)系統(tǒng)內(nèi)存過低的時候,OS將會去廣播低內(nèi)存消息舌胶,通知大家釋放內(nèi)存捆蜀,在時間閥值之后,將會按照隊(duì)列優(yōu)先級去依次kill進(jìn)程
- UIKit 提供了以下回調(diào)和通知去處理低內(nèi)存
[UIApplicationDelegate applicationDidReceiveMemoryWarning:]
[UIViewController didReceiveMemoryWarning:]
/*可以處理單例中大內(nèi)存對象幔嫂,持有的大數(shù)據(jù)image和data的釋放*/
UIApplicationDidReceiveMemoryWarningNotification
/*SDWebImage清除緩存辆它,CacheManager清除緩存*/