操作系統真實的虛擬內存是什麼樣的(三. committed memory)

1. commit limit與current commit charge

接上文,我們看到testlimit -r開關,只是預留虛擬內存,並沒有實際進行提交(commit)。預留虛存並不存儲數據或代碼,但有時候應用需要這個預留(就像預訂坐位一樣),用以創建大塊虛存,並且在需要的時候進行提交,以確保提交的內存在地址空間上是連續的。當進程提交一塊虛存時,操作系統要確保存儲在內存裏的數據要麼全部在內存裏,要麼全部在硬盤中。這意味着進程的運行有另外一個限制:commit limit。


commit limit是物理內存和頁文件大小的總和。事實上,並不是所有物理內存被計算在內,因爲操作系統預留了一部分給自己使用。所有存活進程提交的虛存的總和,稱之爲current commit charge,它不能超過系統的commit limit。當commit limit值達到時,虛存的分配就會失敗。這意味着標準的32位進程,在達到2GB地址空間限制之前,有可能會出現虛存分配失敗。

current commit charge和commit limit由進程管理器來跟蹤,其數據顯示如下圖:

在Vista和Win2008之前的版本,可能顯示的不叫current commit charge和limit,而採用的是current commit change ("PF Usage"),實際上就是page file usage:

在Vista和win2008下,任務管理器不顯示commit charge圖,只給出current commit charge和limit值,表示爲"Page File"


你可以使用Testlimit -m 來對commit limit進行壓力測試,讓它強行分配已提交內存。32位版本的Testlimit在達到commit limit之前可能不會碰到地址空間的級限,這要依賴於物理內存的大小、虛存頁文件的大小以及current commit charge值的大小。如果在32位windows下運行,並且要看看在達到commit limit時系統的行爲,可以多運行幾個Testlimit實例,直到有一個在用盡地址空間之前,達到commit limit。


注意,缺省情況下,頁文件是動態增長的,那意味着commit limit也會動態增長(在commit charge變大並且接近它時),並且,即使頁文件達到它的最大值,系統也會放掉部分內存,進行內部調節,就如同普通的應用程序緩存完數據以後,會放掉一部分內存一樣。Testlimit在達到commit limit之後,會sleep幾秒鐘,接着嘗試分配更多內存,不斷重複,直至被強行終斷。


如果你運行的是64位的Testlimit, 幾乎可以肯定的是,在用盡地址空間之前,它就會達到commit limit,除非物理內存和頁文件大小加起來能超過8TB,這個8TB在第一節裏,已經描述了,它是64位應用可以訪問的地址空間。這裏是64位Testlimit在8GB內存的系統上(指定每次分配100M)的運行過程中的部分結果:




這是當Testlimit停頓下來的提交歷史圖:


當系統虛存接近耗盡時,應用程序可能會失敗,你有可能會看到奇怪的錯誤消息,在大多情況下,Windows將會提示內存不夠。如下圖:


當你退出Testlimit時,commit limit又會降下一點點,因爲內存管理器會把虛存頁文件進行截短,以適應Testlimit的極端的內存提交請求。這裏,進程管理器顯示當前的limit正好在峯值以下。

2. 進程提交的內存

由於commit limit是一個全局資源,它的消耗會導致低性能、應用程序失敗甚至系統失敗,自然就有人問了,諸多進程,對於commit charge有多大貢獻呢?要準確的回答這個問題,我們需要理解應用程序可以分配的不同類型的虛存。

並不是所有由進程分配的虛存都要算到commit limit裏,正如你看到的,預留的虛存就不能算。代表磁盤文件的虛存,或稱爲文件映像視圖,同樣也不算在內,除非應用程序要求copy-on-write(寫時複製),因爲Windows會丟棄物理內存中的數據,並從文件中讀取。Testlimit地址空間中可執行代碼或者系統DLL的鏡像所佔的虛存是不能算到commit limit裏。只有兩種類型的進程虛存要算到commit limit裏:私有虛存和後端頁文件(pagefile-backed)。

私有虛存,主要用於支持堆的垃圾回收、原生的堆和語言裏的內存分配器,稱其爲私有是因爲從定義角度來講,這類虛存在進程間是不能共享的。基於這個原因,它也容易被定義到一個進程裏,便於Windows通過私有的字節性能計數器來追蹤它的使用情況。進程管理器通過Private Bytes列來顯示有多少字節已經使用了,看看下邊的示意圖:


Pagefile-backed(後端支持頁)虛存,相對難以界定,因爲它是進程間共享的,事實上,也沒有與進程相關的計數器來告訴你一個進程分配了或者引用了多少後端支持頁。當你運行Testlimit -s時,它分配後端支持頁虛存,直到達到commit limit,但是,即使耗光了所有的29GB,進程的虛存統計仍然不會指出它是出問題的那個進程:

由於這個原因,添加了-l參數來處理。一個進程必須要打開一個後端支持頁虛存對象,稱爲節,來爲它的地址空間創建一個後端支持頁的虛存映射表。當Windows預留現有虛存(即使某應用關掉了該節的句柄時)時,大多數應用會保持該句柄爲打開狀態。-l開關會打印分配的節的大小。這時使用-s開關的Testlimit的部分輸出結果:

你會看到,Testlimit正在分配以1MB爲單位的塊。

3. 頁文件大小應該建爲多大?

這是經常問的問題,似乎也沒什麼標準答案,甚至微軟也會給出誤導人的建議。幾乎所有的建議都基於RAM大小的若干倍,通常1.2、1.5或者2。既然你知道了頁文件在系統的commit limit中的作用,以及進程對commit charge的作用,你就會知道,這些公式基本沒什麼用。

因爲commit limit設定了所有進程能夠分配的私有虛存和後端支持頁虛存的總和的上限,唯一確定合理頁大小的方法是知道你同時運行的程序的總的commit charge的最大值,如果commit limit比那個值小,你的程序就可能不會正常工作。

如何知道commit charge呢?注意到windows會追蹤一個值:Peak commit charge,要優化頁大小,你應該同時啓動所有應用程序,加載典型的數據集,注意監測commit charge值,把頁大小的最小值設爲該值減去系統的物理內存 (如果值爲負,則挑一個最小值以允許crash dump)。如果你想多一些空間,可以把該值乘以2。


有些人可能會認爲沒有頁文件會達到更好的性能,但是通常情況下,擁有頁文件,意味着windows可以將髒數據回寫到頁文件,從而可以讓那塊內存提供給更多的進程或者緩存。通常情況下,擁有虛存頁意味着可以分配更多的內存給系統使用。

頁文件的配置是系統屬性,運行命令”sysdm.cpl"即可進入配置框。

你會注意到,windows的默認配置是自動管理頁文件大小。針對XP和2003系統,RAM不超過1G時,windows會創建1.5倍大小的頁文件,超過1G時,會創建最小RAM大小的虛存頁文件。在Vista或2008下,最小值是至少能容納一個內核內存崩潰轉儲文件,RAM加上300M,或者是1G,取其中的最大值。最大值是RAM大小的3倍,或者4GB。這也解釋了在8GB內存的64位系統裏,peak commit值最大曾經達到32GB,頁大小的24GB加上物理內存的8GB。


與虛存相關的一些限制,主要是虛存的最大值以及頁文件的數目。32位的windows最大頁文件大小爲16TB(如果在非-PAE模式下,最大也只能是4GB),64位windows可以將頁大小弄到16TB (x64), 在IA64架構下,可以搞到32TB。而Windows8的ARM架構下,頁大小最大隻能是4GB,以所有版本,Winodws支持最多16個頁文件,並且每個頁文件必須位於不同的卷(分區)。具體限制列表如下:

Version

Limit on x86 w/o PAE

Limit on x86 w/PAE

Limit on ARM

Limit on x64

Limit on IA64

Windows 7

4 GB

16 TB

 

16 TB

 

Windows 8

 

16 TB

4 GB

16 TB

 

Windows Server 2008 R2

     

16 TB

32 TB

Windows Server 2012

     

16 TB

 


<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章