第18章堆
先講優缺點,產生興趣,再講堆是什麼,怎麼用。
18.1進程的預設堆
18.2為什麼要建立額外的堆
18.3如何建立額外的堆
18.4其他堆函數
---------標記的為自己的分析。
先說說優缺點:堆非常適合分配大量的小型資料。與虛擬記憶體和記憶體對應檔相比,堆是用來管理鏈表和樹的最佳方式。優點:不必理會分配粒度和頁面邊界這類事情。缺點:分配和釋放記憶體塊的速度比其他方式慢,且無法再對實體儲存體器的調撥和撤銷調撥進行直接控制。
堆是什麼:堆就是一塊預訂的地址空間地區。剛開始,地區內的大部分頁面都沒有調撥實體儲存體器。隨著我們不斷從堆中分配記憶體,堆管理器會給堆調撥越來越多的實體儲存體器。這些實體儲存體器始終是從頁分頁檔中分配的。釋放堆中的記憶體塊時,堆管理器會撤銷已調撥的實體儲存體器。
-------說明有個堆管理器,調撥和釋放實體儲存體器都由堆管理器來控制,而且只能從頁分頁檔中分配。程式員只用從堆中分配記憶體,由堆管理器來調撥和釋放實體儲存體器。
18.1進程的預設堆
定義:進程初始化時,系統會在進程地址空間中建立一個堆。預設大小為1M。
應用:許多windows函數會用到進程的預設堆。當程式有多個線程同時調用windows函數時,對預設堆的訪問必須依次進行。即:系統保證一次只讓一個線程從預設堆中分配或釋放記憶體塊。
一個進程同時可有多個堆,進程在整個生命週期內可建立和銷毀這些堆。但,預設堆在進程開始之前由系統自動建立,進程終止後自動銷毀,不受人為控制。每個堆都有一個用來標識自己的堆控制代碼,所有分配和釋放記憶體塊的堆函數都會在參數中用到這個堆控制代碼。GetProcessHeap可獲得進程的預設堆的控制代碼。---------有啥用,又不能控制。
18.2為什麼要建立額外的堆
這些原因可能為:
對組件進行保護
更有效記憶體管理
局部訪問
避免線程同步的開銷
快速釋放
18.3如何建立額外的堆
HANDLE HeapCreate(
DWORD fdwOptions,
SIZE_T dwInitialSize,//一開始要調撥給堆的位元組數。
SIZE_T dwMaximumSize);//表示堆所能增長到的最大大小。若為0,則堆沒有指定的上限。從堆中分配記憶體會使堆不斷增大,直到用盡所有的實體儲存體器為止。
從堆中分配記憶體時,HeapAlloc函數執行以下操作:
1)遍曆已指派記憶體的鏈表和閑置記憶體的鏈表。
2)找到一塊足夠大的閑置記憶體塊。
3)分配一塊新的記憶體,也就是將剛找到的閑置記憶體塊標記為已指派。
4)將新分配的記憶體塊添加到已指派記憶體的鏈表中。
fdwOptions取值:
HEAP_NO_SERIALIZE:盡量不用。不指定HEAP_NO_SERIALIZE標識時,只允許一個線程獨佔堆及其鏈表的訪問,直到所必須的操作都已經完成(即HeapAlloc的四步操作全部完成)。若且唯若進程中的線程獨佔訪問堆(通過同步機制或者僅有一個線程訪問堆)時,才可指定此屬性。
HEAP_GENERATE_EXCEPTIONS:每當堆中分配或重新分配記憶體塊失敗時,拋出一個異常。
HEAP_CREATE_ENABLE_EXCUTE:可在堆中存放可執行代碼。
18.3.1從堆中分配記憶體塊
PVOID HeapAlloc(
HANDLE hHeap,
DWORD fdwFlags,
SIZE_T dwBytes);
fdwFlags取值:
HEAP_ZERO_MEMORY:在HeapAlloc返回前把記憶體塊的內容都清零。
HEAP_GENERATE_EXCEPTIONS:同HeapCreate。HeapCreate指定時,此處不必指定,整個堆有這個屬性,從此堆分配的記憶體都具有此屬性。HeapCreate未指定,此處指定時,僅分配到達記憶體有此屬性,堆中其他記憶體無此屬性。
HEAP_NO_SERIALIZE:同上,不要用。
在分配大塊記憶體(1MB或更多)時應避免使用堆函數,用VirtualAlloc。
若分配大量不同大小的記憶體塊,那麼堆管理器內部用來處理分配請求的預設演算法可能會產生地址空間片段:由於所有可用記憶體塊都不夠大,因此系統無法找到一塊足夠大的閑置記憶體塊。在xp和server2003之後的版本中,可強制作業系統在分配記憶體時使用一種叫低片段堆的演算法。在多處理器的機器上,低片段堆的效能得到了極大的提高。下面代碼用來切換到低片段堆:
ULONG HeapInformationValue = 2;
if (HeapSetInformation(hHeap, HeapCompataibilityInformation, &HeapInformationValue, sizeof(HeapInformationValue))
{//hHeap is turned into a low fragmentation heap}
else
{//hHeap can’t be turned into a low fragmentation heap.Maybe because it has been created with the HEAP_NO_SERIALIZE flag}參考P498
18.3.2調整記憶體塊的大小
PVOID HeapReAlloc(
HANDLE hHeap,
DWORD fdwFlags,
PVOID pvMem,
SIZE_T dwBytes);
18.3.3獲得記憶體塊的大小
分配一塊記憶體後,可調用HeapSize來得到這塊記憶體的實際大小:
SIZE_T HeapSize(
HANDLE hHeap, //堆標識
DWORD fdwFlags, //0或HEAP_NO_SERIALIZE
LPCVOID pvMem); //記憶體塊的地址
18.3.4釋放記憶體塊
BOOL HeapFree(
HANDLE hHeap,
DWORD fdwFlags, //0或HEAP_NO_SERIALIZE
PVOID pvMem);釋放記憶體可能會使堆管理器撤銷一些已經調撥的實體儲存體器。
18.3.5銷毀堆
BOOL HeapDestroy(HANDLE hHeap);
18.3.6在C++中使用堆:樣本了如何在一個堆中分配所有執行個體。
18.4其他堆函數:P503