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