Windows内存管理 - 隐藏在new和malloc背后的heap
??? 先來說,heap是什么,heap就是堆,在不知道具體細節的時候,我們只知道,通過new和malloc,我們可以動態獲得一個內存區域,用來存放自己的對象和變量,而這些內存區域都是在heap上的。heap應該就是一個內存區域吧。應該會有很多人這么認為過。
??? 后來,學習了Windows的內存模型和內存管理機制,知道了地址空間,明白了Windows(32bit)給每個進程一個假象,就好像是進程擁有一個大到4G的內存空間。這個就是虛擬的地址空間,程序在這個地址空間上訪問數據,而這個數據的在物理內存中的真實位置是不確定的,可能在物理內存的某個頁中,也可能因為換頁,存放在頁面文件中。Windows的核心管理著虛擬地址空間和物理內存的對應關系,而作為程序員,我們所見到,只有虛擬地址而已。
??? 操作系統的內存管理機制的絕妙之處在于,一個進程可以使用的地址空間很大,但是除非這些地址被保留后提交,否則就不會占用真實的物理內存。翻翻MSDN,發現以下這些API:
?VirtualAlloc
?VirtualAllocEx
?VirtualFree
?VirtualFreeEx
?VirtualLock
?VirtualProtect
?VirtualProtectEx
?VirtualQuery
?VirtualQueryEx
?VirtualUnlock
??
?VirtualAlloc和VirtualAllocEx用來在地址空間上保留和提交一個區域。
?VirtualFree和VirtualFreeEx用來釋放或者反提交一個區域。
?VirtualLock用于將一個地址空間上的區域鎖在物理內存上,也就是防止因為換頁而導致的這個區域的數據進入頁面文件。?VirtualUnlock的作用正相反,是將鎖到物理內存上的地址區域解鎖,使之允許被換頁出物理內存。
?VirtualProtect和VirtualProtectEx用于設置一個地址區域的保護屬性。
?VirtualQuery和VirtualQueryEx用于獲取一個地址區域的額外信息。
???
??? 這些函數是Windows API中,我們能夠接觸到的,內存分配的最核心的API了,我們可以對內存分配的過程的許多細節做精細的控制,要考慮的內容也很多,比如頁的大小,內存分配粒度問題。Windows上所有的內存分配都要用到這些函數。
??? 既然內存分配的API都有了,new和malloc有是怎么回事。?
??? 首先,C和C++都是內核非常精簡的語言,關鍵字很少,很多功能都是通過庫函數進行擴充的,而C和C++的運行時庫是這些庫中最不可或缺的一個庫,它負責在C和C++編寫的程序開始執行之前,初始化一個可供程序運行的環境(這個環境包括棧、堆、輸入輸出和環境變量等等),而malloc是運行時庫中負責內存分配的函數。
??? C和C++編程語言要做到跨平臺,當然就需要有針對不同平臺的運行時庫的實現了。malloc函數,要在Windows上面實現,那最終還是需要用到Windows上面的內存分配函數,要在Linux上面實現,那肯定也需要使用Linux的內存分配函數。
??? new和malloc的區別在于,malloc是運行時庫函數,而new是語言的關鍵字,關鍵字是在語言的核心進行支持的,目的是支持類的創建和構造。new在生產一個對象實例的時候,必須判斷類的構造函數,并準確的調用,而光用mallloc分配內存是不夠的。這個是new的特殊之處,但是new的實現中一定也使用了malloc函數。
??? 看似malloc可以直接調用虛擬內存分配的API,申請內存,可是Windows并沒有這么做,而是在地址空間上又加了一個層次,這個就是heap,通過heap來管理一個區域的地址空間,這樣就可以有多個heap,就像把地址空間分成了若干個小的內存池,彼此之間互不影響。
??? 這樣有很多好處,第一,在申請或釋放內存的時候,操作系統要查找未分配或已分配的內存區域的鏈表,這種小型化的內存池相比整個地址空間作為一個完整內存池的方案來說,效率是要高很多的;第二,在多線程運行的環境下,如果線程使用不是同一個heap,那么也就不需要考慮線程同步的問題了,可以提高多線程的效率。其他的好處,恕我愚鈍,還沒想到。
??? 使用heap的好處,我們平常沒有認識到,因為我們太習慣于new和malloc了。有些時候new和malloc不是使用內存的好方案,為什么呢?因為效率。
??
?? C和C++的運行時庫在初始化的時候,會創建一個heap,叫做運行時庫heap。是的,沒錯,在這之后就運行時庫不會再創建其他heap了,也就是說,如果你用malloc申請內存,始終是在這個運行時庫heap上分配內存,無論是一個字節還是一個大數組,這樣一來,這個heap就會很零散。
?? 最要命的是,運行時庫heap是一個序列化的heap,所謂序列化,就是說在這個heap上面的的所有操作必須有先后順序,比如線程A正在用malloc申請內存,這個時候線程B馬上也調用了malloc,但是A的調用還沒結束,線程B只能等待。在單CPU單核的機器上,性能的差異不明顯,在多核多CPU的機器上,這樣的等待如果多了,就會明顯的影響多線程的效率了。
?? 繼續說一些heap的信息。Windows下面,每一個進程都會創建一個默認的heap,初始大小是1MB,Windows的許多函數都需要一些臨時的內存塊,這些都是從進程的默認heap中分配的。如果是一個C或C++程序,就得加上運行時庫heap,這樣最起碼得有2個heap,但是我試著創建一個控制臺程序,用API統計一下heap的數目,竟然有4個heap,不知道其他的heap是什么時候創建的。
?? 對于系統默認的heap的訪問必須有線程保護,也就是順序操作(序列化的heap),這個會浪費一些時間(上面已經說明了)。如果要想使得你的線程以最快的速度訪問heap,你需要為線程創建一個只被自己訪問的heap,并取消heap的序列化選項。但是默認的heap必定是序列化的。
?Windows下面與heap相關API
?GetProcessHeap
?GetProcessHeaps
?HeapAlloc
?HeapCompact
?HeapCreate
?HeapDestroy
?HeapFree
?HeapLock
?HeapQueryInformation
?HeapReAlloc
?HeapSetInformation
?HeapSize
?HeapUnlock
?HeapValidate
?HeapWalk
?
?這里只羅列一下,怎么使用就參考MSDN吧。
?
http://blog.csdn.net/zencher/archive/2010/05/13/5588748.aspx
總結
以上是生活随笔為你收集整理的Windows内存管理 - 隐藏在new和malloc背后的heap的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VirtualAlloc和Virtual
- 下一篇: 属性访问器(Property Acces