windows虛擬內存機制
在 Windows 系統中,每個進程都擁有自己獨立的虛擬地址空間(virtual address space)。虛擬內存機制使得應用程序認為它擁有連續可用的內存(一個連續完整的地址空間),而實際上,它通常被分隔成多個物理內存碎片,還有部分暫時存儲在外部磁盤存儲器上。
以下是 Windows 內存分配過程的三個要點:
- 保留一段虛擬內存地址空間:使用帶
mem_reserve參數的VirtualAlloc函數從進程的 4GB 空間中保留一段地址空間。起始地址必須是系統分配粒度的整數倍(通常為 64KB),大小必須是系統頁面大小的整數倍(通常為 4KB)。 - 提交一段虛擬內存地址空間:通過帶
mem_commit參數的VirtualAlloc函數,將進程已保留的一段地址空間映射到機器的虛擬內存上。起始地址和大小同樣都必須是頁面大小的整數倍(4KB)。 - 將虛擬內存地址空間映射到物理內存頁(RAM):當訪問進程提交的頁面時,如果該頁面不在物理內存頁中,將產生一個缺頁中斷(又名頁缺失、頁面錯誤,page fault),系統會通過該機制真正分配物理內存頁,同時修改對應頁面的地址空間映射關系。
進程地址空間分布(以常見的 2GB 為例):Windows 系統在進程空間中專門劃出一塊 0x70000000–0x80000000(共 256MB)區域,用于映射常用的系統 DLL(如 kernel32.dll、ntdll.dll 等)。并且,系統會對系統 DLL 的默認基地址進行調整,防止加載時沖突,觸發重定基地址(rebasing)。需要注意的是,基地址必須對齊到分配粒度(64KB)。
另外,在生成 EXE 和 DLL 模塊時,可以使用
/dynamicbase鏈接參數啟用動態基地址(Address Space Layout Randomization,ASLR),它可以實現地址空間布局隨機化,防范惡意程序對已知地址進行攻擊。Windows 虛擬內存機制涉及的一些內存指標概念如下:
- 虛擬內存:
private bytes:進程已提交(committed)的虛擬內存字節數,對應vmmap的private、Win7 任務管理器中的【提交大小】、資源管理器中的【提交】。peak private bytes:進程已提交的虛擬內存的最高峰字節數。virtual size:進程保留(reserved)的虛擬地址空間字節數。page faults:發生過的缺頁中斷次數,對應 Win7 任務管理器中的【頁面錯誤】。
- 物理內存:
working set = ws private + ws shareable:進程占用物理內存的總字節數,對應 Win7 任務管理器中的【工作設置 (內存)】、資源管理器中的【工作集】。ws private:進程獨享的物理內存字節數,例如堆內存、棧內存、通過寫時復制(copy-on-write,COW)機制創建的內存等,對應 Win7 任務管理器中的【內存 (專用工作集)】、資源管理器中的【專用】。ws shareable:進程可與其他進程共享的物理內存字節數,例如 EXE 及 DLL 代碼段、數據段等,對應 Win7 資源管理器中的【可共享】。ws shared:進程已與其他進程共享的物理內存字節數,且ws shared <= ws shareable。若只啟動一個 EXE 實例,那么 EXE 的代碼段、數據段等不會被共享,因而就不統計在ws shared中。peak working set:物理內存的最高峰字節數,對應 Win7 任務管理器中的【峰值工作設置 (內存)】。
無論是虛擬內存還是物理內存下的各個指標,通常都是通過統計用戶態的那部分占用情況得出的。
為了擴大地址空間、對特定的內存地址提供寫保護和公平分配內存等,Windows 系統采用了虛擬內存技術。但該技術也存在一些局限性,例如可能會浪費內存、增加指令的執行時間等。
頁交換文件(pagefile)一般被用作可寫物理內存頁的后備存儲器,在 Windows 系統中該文件名為 pagefile.sys,位于各盤的根目錄中。當物理內存不夠時,系統會進行頁出(pageout)操作,將一些不經常使用且有后備的物理內存頁釋放,并根據情況將虛擬地址映射關系指向后備:
- 以頁交換文件(如堆、棧等)為后備:在頁交換文件中分配空間,并拷貝內容到其中后再釋放。
- 以內存映射文件(如 EXE、DLL 等)為后備:直接釋放。
而當系統讀取某個虛擬內存地址,而該地址所在的頁不在物理內存頁中時,將產生一個缺頁中斷,觸發頁入(pagein)操作,告訴系統從頁交換文件或者內存映射文件中取回包含該地址的虛擬內存頁(即將內容拷回到物理內存頁,并建立新的虛擬地址映射到物理內存頁上,然后釋放頁交換文件中對應部分的空間)。
此外,系統在映射 EXE 或 DLL 文件時會把數據頁指定為
page_writecopy屬性,把代碼頁指定為page_execute_writecopy屬性,利用寫時復制機制節省物理內存和頁交換文件的占用。當進程對具有writecopy屬性的內存頁面執行修改操作時,系統會找一個閑置的物理內存頁,并拷貝所有內容到新頁上,然后標記新頁的后備存儲器為頁交換文件,最后將進程的虛擬內存頁指向新的物理內存頁。經過上述步驟,進程就可以使用新的頁面進行操作。在 32 位 Windows 系統中,程序最多能使用 2GB 空間(0x00010000-0x7ffeffff)。若要獲得 3GB 的地址空間,可以參考以下方法:
- 操作系統方面:
- 32 位 Windows XP:無具體方法。
- 32 位 Win7:管理員權限執行命令
bcdedit /set increaseuserva 3072來開啟。 - 64 位 Win7:對 32 位程序默認開啟 3GB,無需額外設置。
- 應用程序方面:無論是 32 位還是 64 位 Windows,若要讓 32 位程序能使用 3GB 內存,必須在鏈接時加上參數
/largeaddressaware。
