詳解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)存的,但沒(mé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頁(yè)。而實(shí)際上其占用的內(nèi)存片段可能是分散的,有可能其占用的真正物理范圍是70-120頁(yè),201頁(yè)到240頁(yè),還有10頁(yè)在磁盤上面。
因此,虛擬內(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í)行沒(méi)有任何影響。
第二類由于磁盤的速度讀寫速度太慢,且很多都會(huì)有一定讀寫次數(shù)的限制,因此,當(dāng)在磁盤上的頁(yè)面要被使用時(shí)候,并非直接在磁盤上修改,而是重新搬運(yùn)回RAM并暫時(shí)凍結(jié)進(jìn)程,搬運(yùn)完成后在RAM內(nèi)被修改。而RAM內(nèi)不活動(dòng)的頁(yè)面也會(huì)在內(nèi)存不足時(shí)候搬運(yùn)到磁盤上,為活動(dòng)的進(jìn)程提供可用的物理內(nèi)存。也就是說(shuō),磁盤相當(dāng)于一個(gè)倉(cāng)庫(kù)而已,真正干活的地方還是在RAM里面。
這種方式使得在一些小內(nèi)存的機(jī)器上也可以運(yùn)行一些占用內(nèi)存大程序,但是不足之處就是慢,卡。
iOS5必定是有第一類虛擬內(nèi)存的,但是沒(méi)有第二類。
首先,如果使用虛擬內(nèi)存,必定會(huì)造成一定的慢,卡,大家在PC上內(nèi)存滿時(shí)候應(yīng)該體會(huì)過(guò)。而這一點(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)的程序提供空間,因而也就是說(shuō)所有的程序都會(huì)在后臺(tái)保留,最終虛擬內(nèi)存占用的磁盤空間也會(huì)越來(lái)越大。而事實(shí)上并沒(méi)有這種情況。而蘋果本身的設(shè)計(jì)也就是允許用戶不去關(guān)閉這些后臺(tái)程序。
當(dāng)然,你也可以認(rèn)為iOS的虛擬內(nèi)存不會(huì)提供給應(yīng)用程序使用。但是如果真的這樣,這虛擬內(nèi)存又有什么用呢?
除此之外,蘋果也在發(fā)布會(huì)后的一次WWDC大會(huì)上說(shuō)了:limited memory/virtual memory/no swapfile。也就是說(shuō)并沒(méi)有通俗意義上的虛擬內(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以上。那這部分空間是在什么地方呢?假如你說(shuō)是在除系統(tǒng)盤和用戶盤以外的地方,好的,這不是不可能,但我們可以算一下。我是32G的,用戶盤大小29754M,系統(tǒng)盤大小1024M,加起來(lái)30778M也就是30G,那剩下的4G往什么地方塞?況且一般來(lái)講由于換算原因和其它因素實(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ù)資料,比如說(shuō)圖片信息之類的,保留最重要的狀態(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)致卡頓。
?、廴绻麊?wèn)題還不能得到解決,系統(tǒng)就會(huì)強(qiáng)制結(jié)束前臺(tái)程序,同時(shí)在/var/logs/AppleSupport/下面留下一堆lowmemory的錯(cuò)誤報(bào)告。這就是常說(shuō)的閃退的一種原因。
由此也可以說(shuō)明,iOS系統(tǒng)的內(nèi)存管理確實(shí)很先進(jìn),確實(shí)是沒(méi)有必要去關(guān)閉后臺(tái)程序。當(dāng)然,如果你認(rèn)為①②步驟導(dǎo)致的小卡讓你很不爽,那你還是主動(dòng)去關(guān)吧。
說(shuō)完了iOS系統(tǒng)的內(nèi)存管理,下面來(lái)說(shuō)一下用deb安裝的虛擬內(nèi)存,也就是真正意義上的虛擬內(nèi)存。
有人說(shuō)開啟這種虛擬內(nèi)存完全沒(méi)有用,只能是使得內(nèi)存看上去增大了很多而實(shí)際上沒(méi)有任何用,還會(huì)導(dǎo)致系統(tǒng)不穩(wěn)。
而我想在此澄清的是:
?、偬摂M內(nèi)存并不能增大你設(shè)備的內(nèi)存,只是為正在運(yùn)行的程序騰出空間。
打個(gè)比方,就是虛擬內(nèi)存并不能增大你工作間的面積,但是它給你提供了一個(gè)倉(cāng)庫(kù),可以將一些當(dāng)前沒(méi)有用的東西搬進(jìn)去放著,這樣你就可以擁有更多空間干你正在干的事,而倉(cāng)庫(kù)到底不是工作的地方。
?、谔摂M內(nèi)存原本是不會(huì)導(dǎo)致設(shè)備系統(tǒng)不穩(wěn)定的,在iOS3時(shí)代用過(guò)的人都應(yīng)該知道,這個(gè)只是在iOS4時(shí)代之后才出現(xiàn)的問(wèn)題。
?、壑劣谔摂M內(nèi)存是否會(huì)影響設(shè)備的壽命,這個(gè)我想應(yīng)該是可以忽略的。我通過(guò)查看一天的內(nèi)存頁(yè)面輸出量,也就相當(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萬(wàn)次,結(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)存并沒(méi)有短缺的時(shí)候,就開始將內(nèi)存內(nèi)一些不活動(dòng)的頁(yè)面寫入磁盤,這樣當(dāng)進(jìn)程需要內(nèi)存時(shí)候,可以直接將這部分分配給進(jìn)程,如果這些不活動(dòng)頁(yè)面沒(méi)有被分配,而占用他們的進(jìn)程又需要修改儲(chǔ)存在其中的數(shù)據(jù),則也可以直接修改,因此唯一可能造成卡頓的操作就是激活有頁(yè)面被交換到磁盤上去的進(jìn)程,而即便這樣,也只需要將磁盤上一部分?jǐn)?shù)據(jù)讀取到內(nèi)存就可以。經(jīng)過(guò)測(cè)試,touch4閃存讀取速度是接近40MB/s,也就是說(shuō),假設(shè)一個(gè)進(jìn)程占用了40M內(nèi)存并被全部交換到閃存里,最多這個(gè)進(jìn)程也就被暫停1s。而事實(shí)上,很少有程序會(huì)占用到40M內(nèi)存,基本上就是游戲,并且一般很少會(huì)全部交換到閃存,就算這樣,激活這個(gè)進(jìn)程也沒(méi)必要把全部頁(yè)面都交換到內(nèi)存里,除此以外,還記得切換程序的過(guò)渡動(dòng)畫嗎,貌似也有1s吧。因此,幾乎感覺(jué)不到卡頓的,就算有一點(diǎn),也沒(méi)關(guān)系啊,總比后臺(tái)被關(guān)掉和前臺(tái)閃退好吧。
因此,虛擬內(nèi)存還是有很大好處的,并不是只是讓內(nèi)存看上去大一點(diǎn)的東西。
下面,我就來(lái)為大家剖析一下deb虛擬內(nèi)存原理是什么。
其實(shí)所有的虛擬內(nèi)存的deb的原理完全一樣,因此橫向比較其穩(wěn)定性沒(méi)有任何意義。簡(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是一樣的。而其他文件不過(guò)就是一些輔助用途,比如fm用來(lái)釋放內(nèi)存,還有一個(gè)關(guān)閉虛擬內(nèi)存加密用的。
下面我們來(lá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í),不過(guò)貌似沒(méi)什么用。
講清楚了這個(gè)程序的作用,下面說(shuō)說(shuō)它的來(lái)歷。
很多人以為這個(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)存,只不過(guò)是被蘋果拿掉了而已。
說(shuō)完了這個(gè),我再給大家講一下虛擬內(nèi)存交換文件的管理方法。
經(jīng)過(guò)我的實(shí)驗(yàn)和查詢一些資料,我總結(jié)出來(lái)了其管理的特點(diǎn)。
其映射方式很有可能直接由內(nèi)存地址映射到閃存的物理地址,也就是說(shuō)其讀寫不用經(jīng)過(guò)文件管理系統(tǒng),直接按照閃存的物理地址寫入。因此你將虛擬內(nèi)存文件刪除不會(huì)影響虛擬內(nèi)存的工作。而其生成這個(gè)文件的唯一目的是占個(gè)位置,讓操作系統(tǒng)和別的程序知道這個(gè)區(qū)域是有用途的,防止其他程序在這塊地址創(chuàng)建文件導(dǎo)致內(nèi)存數(shù)據(jù)被篡改。
除此以外,無(wú)論你怎么設(shè)權(quán)限,就算全部權(quán)限取消,就是每個(gè)用戶組讀取,寫入,執(zhí)行都取消,也不影響虛擬內(nèi)存,交換文件一樣被修改。很有可能其完全不受文件管理系統(tǒng)控制,完全獨(dú)立開來(lái)。因此修改其權(quán)限沒(méi)有什么意義。很多人說(shuō)修改為777,事實(shí)上000還更穩(wěn)定。
最后,也就是大家最關(guān)心的,為什么虛擬內(nèi)存不穩(wěn)定,原理見(jià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)然我還遇到過(guò)所有程序消失之類的現(xiàn)象。
因?yàn)槊看芜M(jìn)程崩潰都會(huì)在/var/logs/AppleSupport下面留下錯(cuò)誤報(bào)告。經(jīng)過(guò)長(zhǎng)時(shí)間的搜集和整理,發(fā)現(xiàn)其主要都是一類錯(cuò)誤,就是SIGABRT或者SIGBUS。這都是常見(jiàn)的內(nèi)存錯(cuò)誤,一般都是由于進(jìn)程請(qǐng)求了一個(gè)錯(cuò)誤的內(nèi)存地址導(dǎo)致的,錯(cuò)誤報(bào)告附帶了這個(gè)地址。我還發(fā)現(xiàn),其請(qǐng)求的地址都是超出了RAM范圍的。也就是說(shuō)其請(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ù)。也就是說(shuō),出現(xiàn)這種情況時(shí)候很可能此時(shí)系統(tǒng)并沒(méi)有中斷,當(dāng)前正在執(zhí)行的任務(wù)沒(méi)有停止,沒(méi)有進(jìn)行數(shù)據(jù)的轉(zhuǎn)移,最終導(dǎo)致內(nèi)存地址出錯(cuò)。這個(gè)同樣可以解釋進(jìn)程出錯(cuò),可能數(shù)據(jù)轉(zhuǎn)移還沒(méi)有完成,原先的任務(wù)卻開始運(yùn)行,此時(shí)溢出的內(nèi)存地址已經(jīng)映射到內(nèi)存區(qū),不會(huì)出現(xiàn)內(nèi)存錯(cuò)誤,但是數(shù)據(jù)轉(zhuǎn)移沒(méi)有完成,也就是說(shuō)這塊區(qū)域的數(shù)據(jù)并不是全是閃存里的數(shù)據(jù),結(jié)果就是進(jìn)程出錯(cuò)。
造成這個(gè)的因素是這樣的。
蘋果為了流暢,反應(yīng)靈敏可謂無(wú)所不用極其。大家也知道ios的用戶界面渲染優(yōu)先級(jí)非常高,完全有可能蘋果直接把用戶界面渲染也作為了一個(gè)中斷。中斷也是有優(yōu)先級(jí)的。如果這個(gè)中斷優(yōu)先級(jí)高于虛擬內(nèi)存的,就可能出現(xiàn)上面講的情況,數(shù)據(jù)還沒(méi)有轉(zhuǎn)移完,虛擬內(nèi)存的任務(wù)卻被停止了,而處理器開始處理用戶界面渲染的任務(wù)。如果剛好用戶界面渲染的數(shù)據(jù)被交換到了閃存上,而沒(méi)來(lái)得及轉(zhuǎn)移到RAM內(nèi),就會(huì)出現(xiàn)內(nèi)存地址溢出或者進(jìn)程出錯(cuò),最典型就是安全模式和花屏。而Springboard和用戶界面渲染關(guān)系最為密切,這樣也可以解釋為什么這個(gè)進(jìn)程崩潰的次數(shù)最多。
要想解決這個(gè)問(wèn)題,就要降低用戶界面渲染優(yōu)先級(jí)或者取消其中斷的權(quán)利,當(dāng)然也可以提高虛擬內(nèi)存中斷的優(yōu)先級(jí)。這一步仍然有待研究來(lái)實(shí)現(xiàn)。