詳解iOS的原生和第三方虛擬內(nèi)存機(jī)制
其實(shí)虛擬內(nèi)存。這項(xiàng)技術(shù)本質(zhì)上就是對(duì)內(nèi)存地址進(jìn)行映射,使得進(jìn)程認(rèn)為自己擁有連續(xù)的,大量的內(nèi)存,提高內(nèi)存利用率,降低程序編寫難度,下面讓我們一起深入的了解iOS的原生和第三方虛擬內(nèi)存機(jī)制吧。
詳解iOS的原生和第三方虛擬內(nèi)存機(jī)制:
虛擬內(nèi)存。這項(xiàng)技術(shù)本質(zhì)上就是對(duì)內(nèi)存地址進(jìn)行映射,使得進(jìn)程認(rèn)為自己擁有連續(xù)的,大量的內(nèi)存,提高內(nèi)存利用率,降低程序編寫難度。因此,虛擬內(nèi)存范疇可以劃分為兩類:第一類:將進(jìn)程占用的內(nèi)存地址映射到RAM內(nèi)其他位置,第二類:將進(jìn)程占用的內(nèi)存地址映射到磁盤上面。iOS5必定是有第一類虛擬內(nèi)存的,但沒有第二類。
首先介紹一下虛擬內(nèi)存。這項(xiàng)技術(shù)本質(zhì)上就是對(duì)內(nèi)存地址進(jìn)行映射,使得進(jìn)程認(rèn)為自己擁有連續(xù)的,大量的內(nèi)存,提高內(nèi)存利用率,降低程序編寫難度。比如一個(gè)程序被系統(tǒng)告知其可用的內(nèi)存片段是0到100頁。而實(shí)際上其占用的內(nèi)存片段可能是分散的,有可能其占用的真正物理范圍是70-120頁,201頁到240頁,還有10頁在磁盤上面。
因此,虛擬內(nèi)存范疇可以劃分為兩類:
第一類:將進(jìn)程占用的內(nèi)存地址映射到RAM內(nèi)其他位置。
第二類:將進(jìn)程占用的內(nèi)存地址映射到磁盤上面。
而我們通俗講的虛擬內(nèi)存就是第二類。
第一類由于都是在RAM內(nèi)進(jìn)行的,速度很快,并且有專門硬件負(fù)責(zé)轉(zhuǎn)換,因而就像是把賓館房間的門牌號(hào)換一下而已,對(duì)程序的執(zhí)行沒有任何影響。
第二類由于磁盤的速度讀寫速度太慢,且很多都會(huì)有一定讀寫次數(shù)的限制,因此,當(dāng)在磁盤上的頁面要被使用時(shí)候,并非直接在磁盤上修改,而是重新搬運(yùn)回RAM并暫時(shí)凍結(jié)進(jìn)程,搬運(yùn)完成后在RAM內(nèi)被修改。而RAM內(nèi)不活動(dòng)的頁面也會(huì)在內(nèi)存不足時(shí)候搬運(yùn)到磁盤上,為活動(dòng)的進(jìn)程提供可用的物理內(nèi)存。也就是說,磁盤相當(dāng)于一個(gè)倉(cāng)庫(kù)而已,真正干活的地方還是在RAM里面。
這種方式使得在一些小內(nèi)存的機(jī)器上也可以運(yùn)行一些占用內(nèi)存大程序,但是不足之處就是慢,卡。
iOS5必定是有第一類虛擬內(nèi)存的,但是沒有第二類。
首先,如果使用虛擬內(nèi)存,必定會(huì)造成一定的慢,卡,大家在PC上內(nèi)存滿時(shí)候應(yīng)該體會(huì)過。而這一點(diǎn)正是蘋果所不愿意的。蘋果一定要讓一項(xiàng)技術(shù)可以流暢的在設(shè)備上運(yùn)行時(shí)候才讓它出現(xiàn)。這個(gè)很好理解,多任務(wù)就是這樣的。
其次,設(shè)備會(huì)在內(nèi)存不足時(shí)候自動(dòng)關(guān)掉一些后臺(tái)程序,如果使用了這項(xiàng)技術(shù),就不會(huì)出現(xiàn)內(nèi)存不足的情況,一旦內(nèi)存不足,系統(tǒng)會(huì)自動(dòng)將一些不活動(dòng)進(jìn)程在內(nèi)存里數(shù)據(jù)搬到磁盤里,為活動(dòng)的程序提供空間,因而也就是說所有的程序都會(huì)在后臺(tái)保留,最終虛擬內(nèi)存占用的磁盤空間也會(huì)越來越大。而事實(shí)上并沒有這種情況。而蘋果本身的設(shè)計(jì)也就是允許用戶不去關(guān)閉這些后臺(tái)程序。
當(dāng)然,你也可以認(rèn)為iOS的虛擬內(nèi)存不會(huì)提供給應(yīng)用程序使用。但是如果真的這樣,這虛擬內(nèi)存又有什么用呢?
除此之外,蘋果也在發(fā)布會(huì)后的一次WWDC大會(huì)上說了:limited memory/virtual memory/no swapfile。也就是說并沒有通俗意義上的虛擬內(nèi)存。
至于一些開發(fā)者發(fā)現(xiàn)在terminal里面輸入top時(shí)候有一個(gè)VM的數(shù)值,并懷疑它是虛擬內(nèi)存大小。那個(gè)具體是什么我也不知道,但是我認(rèn)為并不是的。那個(gè)數(shù)值確實(shí)會(huì)隨著程序開的越多而越大,甚至可以到達(dá)4G。
下面是我分析的辦法。我將用戶盤和系統(tǒng)盤全部塞滿,發(fā)現(xiàn)系統(tǒng)仍然可以正常運(yùn)行。當(dāng)我打開那些程序的時(shí)候,VM的數(shù)值同樣增大,最終同樣可以到4G以上。那這部分空間是在什么地方呢?假如你說是在除系統(tǒng)盤和用戶盤以外的地方,好的,這不是不可能,但我們可以算一下。我是32G的,用戶盤大小29754M,系統(tǒng)盤大小1024M,加起來30778M也就是30G,那剩下的4G往什么地方塞?況且一般來講由于換算原因和其它因素實(shí)際可用空間都會(huì)小于稱標(biāo)空間的。
至于iOS系統(tǒng)的內(nèi)存管理究竟是怎么樣的呢?據(jù)我推測(cè)是這樣的。
?、佼?dāng)內(nèi)存不足時(shí)候,首先會(huì)先叫后臺(tái)程序或者系統(tǒng)進(jìn)程釋放。此時(shí)后臺(tái)程序會(huì)主動(dòng)釋放一些不太重要的數(shù)據(jù)資料,比如說圖片信息之類的,保留最重要的狀態(tài)信息,與此同時(shí)也可能對(duì)內(nèi)存數(shù)據(jù)進(jìn)行壓縮。此時(shí),由于占用處理器資源,可能會(huì)出現(xiàn)卡頓。
②當(dāng)內(nèi)存依然不足時(shí),系統(tǒng)便開始考慮關(guān)閉一些后臺(tái)程序了。此時(shí),后臺(tái)程序會(huì)得到信號(hào),然后開始運(yùn)行,進(jìn)行數(shù)據(jù)的保存,完成后退出,釋放內(nèi)存。此時(shí),由于會(huì)占用處理器以及儲(chǔ)存器,可能會(huì)再次導(dǎo)致卡頓。
?、廴绻麊栴}還不能得到解決,系統(tǒng)就會(huì)強(qiáng)制結(jié)束前臺(tái)程序,同時(shí)在/var/logs/AppleSupport/下面留下一堆lowmemory的錯(cuò)誤報(bào)告。這就是常說的閃退的一種原因。
由此也可以說明,iOS系統(tǒng)的內(nèi)存管理確實(shí)很先進(jìn),確實(shí)是沒有必要去關(guān)閉后臺(tái)程序。當(dāng)然,如果你認(rèn)為①②步驟導(dǎo)致的小卡讓你很不爽,那你還是主動(dòng)去關(guān)吧。
說完了iOS系統(tǒng)的內(nèi)存管理,下面來說一下用deb安裝的虛擬內(nèi)存,也就是真正意義上的虛擬內(nèi)存。
有人說開啟這種虛擬內(nèi)存完全沒有用,只能是使得內(nèi)存看上去增大了很多而實(shí)際上沒有任何用,還會(huì)導(dǎo)致系統(tǒng)不穩(wěn)。
而我想在此澄清的是:
①虛擬內(nèi)存并不能增大你設(shè)備的內(nèi)存,只是為正在運(yùn)行的程序騰出空間。
打個(gè)比方,就是虛擬內(nèi)存并不能增大你工作間的面積,但是它給你提供了一個(gè)倉(cāng)庫(kù),可以將一些當(dāng)前沒有用的東西搬進(jìn)去放著,這樣你就可以擁有更多空間干你正在干的事,而倉(cāng)庫(kù)到底不是工作的地方。
?、谔摂M內(nèi)存原本是不會(huì)導(dǎo)致設(shè)備系統(tǒng)不穩(wěn)定的,在iOS3時(shí)代用過的人都應(yīng)該知道,這個(gè)只是在iOS4時(shí)代之后才出現(xiàn)的問題。
?、壑劣谔摂M內(nèi)存是否會(huì)影響設(shè)備的壽命,這個(gè)我想應(yīng)該是可以忽略的。我通過查看一天的內(nèi)存頁面輸出量,也就相當(dāng)于寫入閃存的數(shù)據(jù)量。如果不開虛擬內(nèi)存大概是幾MB,如果開啟大概是200MB左右。如果你開啟的大概是256MB,也就是平均這個(gè)區(qū)域一天才能全部寫滿一次。
當(dāng)然也有的鋒友擔(dān)心的是對(duì)同一個(gè)區(qū)塊反復(fù)擦寫。其實(shí)這個(gè)是不必?fù)?dān)心的,因?yàn)殚W存有損耗平衡,它會(huì)盡量少寫入擦寫次數(shù)多的地方,并且每次重啟虛擬內(nèi)存文件都是重新創(chuàng)建的。除此以外只有在內(nèi)存不足的時(shí)候才會(huì)寫入閃存,而最主要的讀取是不會(huì)影響壽命的。而nand閃存寫入次數(shù)大概是10萬次,結(jié)合總?cè)萘?,看看有多大影?
我們使用虛擬內(nèi)存的主要目的是給當(dāng)前運(yùn)行的程序提供更多的物理內(nèi)存,防止出現(xiàn)系統(tǒng)由于內(nèi)存不足采取的措施導(dǎo)致的卡頓和閃退,當(dāng)然也可以在后臺(tái)運(yùn)行更多的程序。
使用虛擬內(nèi)存一定程度上可能會(huì)導(dǎo)致切換程序的卡頓。此時(shí)系統(tǒng)正在將磁盤內(nèi)的數(shù)據(jù)轉(zhuǎn)移到內(nèi)存。
UNIX的虛擬內(nèi)存是這樣的,在內(nèi)存并沒有短缺的時(shí)候,就開始將內(nèi)存內(nèi)一些不活動(dòng)的頁面寫入磁盤,這樣當(dāng)進(jìn)程需要內(nèi)存時(shí)候,可以直接將這部分分配給進(jìn)程,如果這些不活動(dòng)頁面沒有被分配,而占用他們的進(jìn)程又需要修改儲(chǔ)存在其中的數(shù)據(jù),則也可以直接修改,因此唯一可能造成卡頓的操作就是激活有頁面被交換到磁盤上去的進(jìn)程,而即便這樣,也只需要將磁盤上一部分?jǐn)?shù)據(jù)讀取到內(nèi)存就可以。經(jīng)過測(cè)試,touch4閃存讀取速度是接近40MB/s,也就是說,假設(shè)一個(gè)進(jìn)程占用了40M內(nèi)存并被全部交換到閃存里,最多這個(gè)進(jìn)程也就被暫停1s。而事實(shí)上,很少有程序會(huì)占用到40M內(nèi)存,基本上就是游戲,并且一般很少會(huì)全部交換到閃存,就算這樣,激活這個(gè)進(jìn)程也沒必要把全部頁面都交換到內(nèi)存里,除此以外,還記得切換程序的過渡動(dòng)畫嗎,貌似也有1s吧。因此,幾乎感覺不到卡頓的,就算有一點(diǎn),也沒關(guān)系啊,總比后臺(tái)被關(guān)掉和前臺(tái)閃退好吧。
因此,虛擬內(nèi)存還是有很大好處的,并不是只是讓內(nèi)存看上去大一點(diǎn)的東西。
下面,我就來為大家剖析一下deb虛擬內(nèi)存原理是什么。
其實(shí)所有的虛擬內(nèi)存的deb的原理完全一樣,因此橫向比較其穩(wěn)定性沒有任何意義。簡(jiǎn)而言之,其功能只是開啟了系統(tǒng)原生就有的功能而已。
所有的虛擬內(nèi)存deb解包后都有一個(gè)放在/system/library/launchdeamons/里面的一個(gè)plist文件。這個(gè)路徑存放的是所有開機(jī)啟動(dòng)的進(jìn)程配置文件。一般這個(gè)plist指向啟動(dòng)的程序就是在/sbin/里面的dynamic_pager。也有的是指向vm,而這個(gè)vm就是deb安裝后放在sbin里面的一個(gè)程序,本質(zhì)上和dynamic_pager是一樣的。而其他文件不過就是一些輔助用途,比如fm用來釋放內(nèi)存,還有一個(gè)關(guān)閉虛擬內(nèi)存加密用的。
下面我們來講一講這個(gè)dynamic_pager到底是個(gè)什么東西。
其實(shí)它并不是虛擬內(nèi)存的進(jìn)程,虛擬內(nèi)存不需要進(jìn)程,是操作系統(tǒng)的功能。這個(gè)進(jìn)程的功能是和系統(tǒng)通信,負(fù)責(zé)創(chuàng)建,刪除虛擬內(nèi)存文件。如果你強(qiáng)行干掉這個(gè)進(jìn)程,虛擬內(nèi)存仍然可用,但不能增加減少交換文件數(shù)量。一旦交換文件寫滿,當(dāng)前的程序會(huì)卡死。
在terminal里面登陸root后直接輸入dynamic_pager回車就可以開啟虛擬內(nèi)存。這個(gè)進(jìn)程有這么幾個(gè)選項(xiàng):
-F 單個(gè)虛擬內(nèi)存交換文件大小,默認(rèn)為64m,使用時(shí)候在后面輸入文件字節(jié)數(shù)。
-S 虛擬內(nèi)存交換文件路徑和名稱,默認(rèn)在/var/vm,默認(rèn)文件名swapfile編號(hào)。
-H 設(shè)置當(dāng)swapfile的總剩余空間低于多少字節(jié)時(shí)候創(chuàng)建新的交換文件。
-L 設(shè)置swapfile總剩余空間多于多少字節(jié)時(shí)刪除空閑的交換文件
-P 優(yōu)先級(jí),不過貌似沒什么用。
講清楚了這個(gè)程序的作用,下面說說它的來歷。
很多人以為這個(gè)是系統(tǒng)自帶的,其實(shí)不是的。這是越獄后cydia自動(dòng)安裝上去的。大家打開cydia,在剛越獄完后就安裝的軟件包里面可以找到,有一個(gè)點(diǎn)開后在文件系統(tǒng)一項(xiàng)可一看到這個(gè)程序。
總之,這個(gè)dynamic_pager是十分重要的,盡管不是原生的,但是由其作用是開啟系統(tǒng)的虛擬內(nèi)存,我們可以知道,iOS原生就支持虛擬內(nèi)存,只不過是被蘋果拿掉了而已。
說完了這個(gè),我再給大家講一下虛擬內(nèi)存交換文件的管理方法。
經(jīng)過我的實(shí)驗(yàn)和查詢一些資料,我總結(jié)出來了其管理的特點(diǎn)。
其映射方式很有可能直接由內(nèi)存地址映射到閃存的物理地址,也就是說其讀寫不用經(jīng)過文件管理系統(tǒng),直接按照閃存的物理地址寫入。因此你將虛擬內(nèi)存文件刪除不會(huì)影響虛擬內(nèi)存的工作。而其生成這個(gè)文件的唯一目的是占個(gè)位置,讓操作系統(tǒng)和別的程序知道這個(gè)區(qū)域是有用途的,防止其他程序在這塊地址創(chuàng)建文件導(dǎo)致內(nèi)存數(shù)據(jù)被篡改。
除此以外,無論你怎么設(shè)權(quán)限,就算全部權(quán)限取消,就是每個(gè)用戶組讀取,寫入,執(zhí)行都取消,也不影響虛擬內(nèi)存,交換文件一樣被修改。很有可能其完全不受文件管理系統(tǒng)控制,完全獨(dú)立開來。因此修改其權(quán)限沒有什么意義。很多人說修改為777,事實(shí)上000還更穩(wěn)定。
最后,也就是大家最關(guān)心的,為什么虛擬內(nèi)存不穩(wěn)定,原理見下。
虛擬內(nèi)存造成系統(tǒng)不穩(wěn)定的直接原因就是重要進(jìn)程崩潰和出錯(cuò)。
崩潰還算一種比較好的結(jié)果。
如果是普通程序,就是閃退,safari最典型。
如果是springboard,就是安全模式。
如果是launch,就是重啟。
進(jìn)程出錯(cuò)可能會(huì)導(dǎo)致當(dāng)機(jī),而最為嚴(yán)重的是對(duì)某些文件的錯(cuò)誤修改,也就是虛擬內(nèi)存導(dǎo)致白蘋果的重要原因,當(dāng)然我還遇到過所有程序消失之類的現(xiàn)象。
因?yàn)槊看芜M(jìn)程崩潰都會(huì)在/var/logs/AppleSupport下面留下錯(cuò)誤報(bào)告。經(jīng)過長(zhǎng)時(shí)間的搜集和整理,發(fā)現(xiàn)其主要都是一類錯(cuò)誤,就是SIGABRT或者SIGBUS。這都是常見的內(nèi)存錯(cuò)誤,一般都是由于進(jìn)程請(qǐng)求了一個(gè)錯(cuò)誤的內(nèi)存地址導(dǎo)致的,錯(cuò)誤報(bào)告附帶了這個(gè)地址。我還發(fā)現(xiàn),其請(qǐng)求的地址都是超出了RAM范圍的。也就是說其請(qǐng)求的是被交換到閃存上的部分。
一個(gè)開啟了虛擬內(nèi)存的機(jī)器,當(dāng)出現(xiàn)這種情況時(shí),系統(tǒng)會(huì)檢測(cè)出進(jìn)程請(qǐng)求的地址溢出,此時(shí)會(huì)出現(xiàn)中斷,也就是處理器停止處理當(dāng)前正在處理的任務(wù),轉(zhuǎn)而處理一個(gè)臨時(shí)新增的任務(wù),也就是將這個(gè)地址映射到的磁盤區(qū)域的數(shù)據(jù)轉(zhuǎn)移到內(nèi)存里面,然后再恢復(fù)之前的任務(wù)。也就是說,出現(xiàn)這種情況時(shí)候很可能此時(shí)系統(tǒng)并沒有中斷,當(dāng)前正在執(zhí)行的任務(wù)沒有停止,沒有進(jìn)行數(shù)據(jù)的轉(zhuǎn)移,最終導(dǎo)致內(nèi)存地址出錯(cuò)。這個(gè)同樣可以解釋進(jìn)程出錯(cuò),可能數(shù)據(jù)轉(zhuǎn)移還沒有完成,原先的任務(wù)卻開始運(yùn)行,此時(shí)溢出的內(nèi)存地址已經(jīng)映射到內(nèi)存區(qū),不會(huì)出現(xiàn)內(nèi)存錯(cuò)誤,但是數(shù)據(jù)轉(zhuǎn)移沒有完成,也就是說這塊區(qū)域的數(shù)據(jù)并不是全是閃存里的數(shù)據(jù),結(jié)果就是進(jìn)程出錯(cuò)。
造成這個(gè)的因素是這樣的。
蘋果為了流暢,反應(yīng)靈敏可謂無所不用極其。大家也知道ios的用戶界面渲染優(yōu)先級(jí)非常高,完全有可能蘋果直接把用戶界面渲染也作為了一個(gè)中斷。中斷也是有優(yōu)先級(jí)的。如果這個(gè)中斷優(yōu)先級(jí)高于虛擬內(nèi)存的,就可能出現(xiàn)上面講的情況,數(shù)據(jù)還沒有轉(zhuǎn)移完,虛擬內(nèi)存的任務(wù)卻被停止了,而處理器開始處理用戶界面渲染的任務(wù)。如果剛好用戶界面渲染的數(shù)據(jù)被交換到了閃存上,而沒來得及轉(zhuǎn)移到RAM內(nèi),就會(huì)出現(xiàn)內(nèi)存地址溢出或者進(jìn)程出錯(cuò),最典型就是安全模式和花屏。而Springboard和用戶界面渲染關(guān)系最為密切,這樣也可以解釋為什么這個(gè)進(jìn)程崩潰的次數(shù)最多。
要想解決這個(gè)問題,就要降低用戶界面渲染優(yōu)先級(jí)或者取消其中斷的權(quán)利,當(dāng)然也可以提高虛擬內(nèi)存中斷的優(yōu)先級(jí)。這一步仍然有待研究來實(shí)現(xiàn)。