Windows內存小結

之前寫過一篇 理解程序內存 , 當時主要是針對用戶態,下面再稍微深刻一點:


咱們以32位程序爲例(不啓用AWE), 總共4G虛擬空間,其中低2G屬於用戶態, 高2G屬於操做系統內核, 每一個程序都有本身的低2G用戶空間, 高2G內核空間是全部程序共享的。 高2G內核空間中, 屬於同一Session的程序又共享相同的session空間:


x86系統全部的內存以64K邊界粒度, 4K頁面大小分配。


用戶態的內存空間,按用途分能夠分爲: image, mapped file, heap, stack, free等
按狀態分能夠分爲:Free, reserved, commit;
Commit的內存,在被訪問時又可能以不一樣的狀態存在, 多是已經提交到物理內存(RAM),也多是已頁文件的形式存在後臺, 若是是頁文件形式,訪問時會觸發換頁操做。


咱們平時以任務管理器或者Process Explorer, 常常會看到一些不一樣內存術語:
virtual size: reserve和commit的虛擬內存
Private bytes: 已經commit的私有虛擬內存
working set: commit的虛擬內存中已經被加載到物理內存中的部分
WS private /  內存(專用工做集) : 不能和其餘程序共享的working set


這些內存的大小關係怎麼樣?
virtual size 確定是最大的; WS private確定是最小的;working set和private bytes大小很差定, 由於working set雖然是表示物理內存, 但它包含共享和非共享兩部分, 而private bytes雖然是虛擬內存,卻只包含私有部分。
另外咱們平時看程序的內存泄漏,主要能夠看private bytes 和 WS private.


咱們程序裏使用的虛擬地址, 它在訪問時是如何別轉成真正的物理地址的?

1. 咱們的虛擬地址被分爲頁目錄索引,頁表索引,字節偏移三部分
2. 根據CR3寄存器獲得當前進程的頁目錄表地址, 根據頁目錄索引獲得頁目錄表項目(PDE), 而後就能夠獲得該頁表的地址
3. 根據頁表索引,獲得頁表項目(PTE)的地址, 而後便可定位到該頁面, 根據偏移字節便可訪問真正的物理內存


操做系統採用按需換頁的算法來實現內存的訪問, 也就是說系統會在真正訪問一個地址的時候纔會把該地址轉成有效的物理地址, 若是訪問失敗, 會觸發換頁異常, 再真正加載該頁面換到物理內存。系統用虛擬地址描述符(VAD, virtual address descriptor)組成的平衡二叉樹來跟蹤全部的虛擬內存,以肯定全部虛擬內存的狀態(free, reserver, commit)和屬性。


下面說下應用層對程序內存的訪問 , 按照內存的用途就能夠大概劃分:
Image: 主要是指二進制模塊在內存中存在方式, 好比Exe和Dll, 對應的API好比LoadLibrary。
Mapped file: 主要是指內存映射文件, 能夠用來快速的加載大文件 ,或者跨進程共享內存, 對應的API好比 CreateFileMapping.
Stack: 每一個線程都有本身的堆棧, 包括用戶態堆棧和內核堆棧,雖然堆棧內存分配有大小限制, 可是很是高效, 函數的局部變量都存在裏面, 程序的運行過程(函數的調用過程)其實是不停的壓棧和出棧的過程,大小通常默認保留1M(參見線程堆棧是如何增加的 )
Heap: 系統有本身的堆管理器, 雖然效率堆內存分配效率低, 可是沒有大小限制, 對應的API好比new, malloc, HeapAlloc


操做系統爲咱們訪問內存提供了各類渠道,咱們能夠根據須要本身選擇, 由下往上能夠分爲:
虛擬內存: 對應的API如VirtualAlloc(Ex), VirtualFree(Ex), VirtualLock, VirtualProtect, 經過這些API,咱們能夠直接分配(reserver, commit)大塊內存( 4K頁面大小), 同時定義修改頁面屬性, 這是最高效的大內存分配方式。
Win32 堆內存: 對應的API如HeapCreate, HeapAlloc, 堆內存創建在虛擬內存之上,不少時候咱們不須要虛擬內存的大塊內存,只須要小塊內存,操做系統經過堆管理器幫咱們解決了這個問題。每一個進程啓動時系統都會建立一個默認堆,同時咱們也能夠建立本身的私有堆, 不一樣模塊之間是否共享同一個CRT堆取決於模塊的編譯選項,(參見 基於WinDbg的內存泄漏分析
CRT 堆內存:C/C++代碼中咱們最經常使用的內存分配方式是malloc和new, 一般狀況下malloc只負責內存分配, 而new在調用malloc分配內存的同時還有在分配的內存上構造對象的功能。至於malloc的實現方式, 不一樣的編譯器廠商會有不一樣的實現, 有些多是經過Win32堆實現,也多是經過虛擬內存API直接實現。


思考爲何有了虛擬內存API和Win32堆API,還要有CRT堆API?
軟件工程裏一條比較經典的話是: 任何問題均可以加一個間接層加以解決。操做系統提供的API都是平臺相關的, 經過CRT這個間接層實現了平臺無關, 同時咱們能夠在這個間接層上作不少事情, 好比內存泄漏跟蹤, 實現本身的內存池等。


若是咱們直接調用虛擬內存API分配內存, 這種內存屬於那種類型?
實際上按照VMMap的說法, 內存類型還有更多: Image, Mapped File, Shareable, Heap, Managed Heap, Stack, Private Data, Page Table, Unusable, Free.
直接經過VirtualAlloc分配的內存不屬於Heap, 應該屬於Private Data.
相關文章
相關標籤/搜索