日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

《Windows核心編程系列》十四談?wù)勀J(rèn)堆和自定義堆

 求真我 2014-04-20

                           堆

     前面我們說過堆非常適合分配大量的小型數(shù)據(jù)。使用堆可以讓程序員專心解決手頭的問題,而不必理會分配粒度和頁面邊界之類的事情。因此堆是管理鏈表和數(shù)的最佳方式。但是堆進(jìn)行內(nèi)存分配和釋放時的速度比其他方式都慢,而且無法對物理存儲器的調(diào)撥和撤銷調(diào)撥進(jìn)行控制。

 

     什么是堆?

 

     在系統(tǒng)內(nèi)部堆就是一塊預(yù)定的地址空間區(qū)域。剛開始堆的大部分頁面都沒有調(diào)撥物理存儲器。隨著我們不斷的從堆中分配內(nèi)存,堆管理器會給堆調(diào)撥越來越多的物理存儲器。這些物理存儲器始終是從頁交換文件中分配的。釋放堆中的內(nèi)存時,堆管理器會撤銷已調(diào)撥的物理存儲器。

 

     進(jìn)程默認(rèn)堆。

 

     進(jìn)程初始化時,系統(tǒng)會在進(jìn)程的地址空間中創(chuàng)建一個堆。這個堆被稱為進(jìn)程的默認(rèn)堆。默認(rèn)情況下,這個堆的地址空間區(qū)域大小是1MB。程序員可以控制這個大小。我們可以在創(chuàng)建應(yīng)用程序時用/HEAP連接器開關(guān)來改變默認(rèn)堆的大小。由于DLL沒有與之關(guān)聯(lián)的堆,因此在創(chuàng)建DLL時,不應(yīng)該使用/HEAP開關(guān)。

 

  

  1. /HEAP:reserve[,commit]  


 

     由于許多Windows函數(shù)會用到進(jìn)程默認(rèn)堆,因此對默認(rèn)堆的訪問必須一次進(jìn)行。系統(tǒng)會保證在任何情況下只讓一個線程從默認(rèn)堆中分配或釋放內(nèi)存塊。如果應(yīng)用程序只有一個線程,而我們又希望以最快的方式訪問堆,我們應(yīng)該創(chuàng)建自己的堆,而不要使用默認(rèn)堆。

 

     默認(rèn)堆由系統(tǒng)創(chuàng)建和釋放,我們無法銷毀默認(rèn)堆。每個堆都有一個標(biāo)識自己的句柄,所有分配和釋放內(nèi)存塊的堆函數(shù)都會在參數(shù)中使用到這個堆句柄。我們可以調(diào)用GetProcessHeap來得到默認(rèn)堆的句柄。

 

 

  1. HANDLE GetProcessHeap();  


 

     創(chuàng)建額外堆的時機(jī):

 

     一:對數(shù)據(jù)保護(hù)。創(chuàng)建兩個或多個獨立的堆,每個堆保存不同的結(jié)構(gòu),對兩個堆分別操作,可以使問題局部化。

 

     二:更有效的內(nèi)存管理。創(chuàng)建額外的堆,管理同樣大小的對象。這樣在釋放一個空間后可以剛好容納另一個對象。

 

     三:內(nèi)存訪問局部化。將需要同時訪問的數(shù)據(jù)放在相鄰的區(qū)域,可以減少缺頁中斷的次數(shù)。

 

     四:避免線程同步開銷。默認(rèn)堆的訪問是依次進(jìn)行的。堆函數(shù)必須執(zhí)行額外的代碼來保證線程安全性。通過創(chuàng)建額外的堆可以避免同步開銷。

 

     五:快速釋放。我們可以直接釋放整個堆而不需要手動的釋放每個內(nèi)存塊。這不但極其方便,而且還可以更快的運行。

 

     創(chuàng)建額外的堆

 

     我們可以調(diào)用HeapCreate來創(chuàng)建額外的堆:

 

 

  1. HANDLE HeapCreate(  
  2.   
  3.      DWORD fdwOptions,  
  4.   
  5.      SIZE_T dwInitilialize,  
  6.   
  7.      SIZE_T dwMaximumSize);  


 

     fdwOptions表示對堆的操作該如何進(jìn)行??梢詡魅?span style="font-family:Times New Roman">0,   HEAP_NO_SERIALIZE,

HEAP_GENERATE_EXCEPTIONS,HEAP_CREATE_ENABLE_EXECUTE或這些標(biāo)志的組合。

HEAP_NO_SERIALIZE告訴堆管理器堆管理器不負(fù)責(zé)堆的線程安全性。對堆的線程安全性的控制由程序員控制。

 

     HEAP_GENERATE_EXCEPTIONS標(biāo)志告訴系統(tǒng),每當(dāng)在堆中分配或重新分配內(nèi)存塊失敗時拋出一個異常。用來通知應(yīng)用程序有錯誤發(fā)生。

 

     dwInitialSize表示一開始要調(diào)撥給堆的字節(jié)數(shù)。如果需要HeapCreate會把這個值向上取整到cpu頁面大小的整數(shù)倍。

 

     dwMaximumSize表示堆所能增長到的最大大小。即系統(tǒng)為堆所預(yù)定的地址空間的最大大小。如果試圖分配的內(nèi)存塊超過最大大小,分配操作會失敗。如果dwMaximumSize0,則表明創(chuàng)建的堆是可增長的,沒有一個指定上限。

 

     函數(shù)執(zhí)行成功HeapCreate會返回一個句柄,標(biāo)識了新創(chuàng)建的堆。

 

堆創(chuàng)建后,需要從堆中分配內(nèi)存時,要調(diào)用HeapAlloc函數(shù):

 

 

  1. PVOID HeapAlloc(  
  2.   
  3.      HANDLE hHeap,  
  4.   
  5.      DWORD fdwFlags,  
  6.   
  7.      SIZE_T dwBytes);  


 

     hHeap是一個堆句柄,表示要從哪個堆分配內(nèi)存。

 

     fdwFlags用來執(zhí)行一些標(biāo)志。這些標(biāo)志會對分配產(chǎn)生一些影響。總共有三個標(biāo)志:

 

          HEAP_ZERO_MEMORY,HEAP_GENERATE_EXCEPTIONSHEAP_NO_SERIALIZE。

 

     HEAP_ZERO_MEMORY會讓HeapAlloc返回之前把內(nèi)存塊的內(nèi)容都清。

 

     HEAP_GENERATE_EXCEPTIONS用來告訴系統(tǒng)如果堆中沒有足夠的內(nèi)存來滿足分配請求,此次調(diào)用的

HeapAlloc應(yīng)拋出異常??梢栽趧?chuàng)建堆時指定這個標(biāo)志,只要在這個堆上分配內(nèi)存,如果內(nèi)存不足都拋出異常。

 

     如果分配成功HeapAlloc會返回內(nèi)存塊地址。否則將會返回NULL

 

     默認(rèn)情況下,對堆的訪問會依次進(jìn)行。當(dāng)任何程序試圖從堆中分配一塊內(nèi)存時,HeapAlloc會執(zhí)行以下操作:

 

     1:遍歷已分配的內(nèi)存的鏈表和閑置內(nèi)存的鏈表。

 

     2:找到一塊足夠大的閑置內(nèi)存塊。

 

     3:分配一塊新的內(nèi)存,將2找到的內(nèi)存塊標(biāo)記為已分配。

 

     4:將新分配的內(nèi)存塊添加到已分配的鏈表中。

 

     注意:在分配大于1MB的內(nèi)存時應(yīng)該避免使用堆函數(shù),而應(yīng)該使用VirtualAlloc函數(shù)。

 

     HeapReAlloc可以改變堆中某一塊內(nèi)存的大?。?/span>

 

 

  1. PVOID HeapReAlloc(  
  2.   
  3.      HANDLE hHeap,  
  4.   
  5.      DWORD fdwFlags,  
  6.   
  7.      PVOID pvMem,  
  8.   
  9.      SIZE_T dwBytes);  


 

hHeap用來標(biāo)識一個堆。

 

fdwFlags用來在調(diào)整內(nèi)存塊大小時用到這些標(biāo)志??梢杂幸韵聵?biāo)志:HEAP_GENERATE_EXCEPTIONSHEAP_NO_SERIALIZE,HEAP_ZERO_MEMORY,HEAP_REALLOC_IN_PLACE_ONLY。

 

前兩個標(biāo)志與前面介紹的一樣。只有當(dāng)增大內(nèi)存塊時HEAP_ZERO_MEMORY才有用。額外的字節(jié)會被清0

 

在增大內(nèi)存塊時HeapReAlloc可能會移動內(nèi)存塊,HEAP_REALLOC_IN_PLACE_ONLY標(biāo)志告訴HeapReAlloc盡量不要移動內(nèi)存塊。如果不移動不能增大內(nèi)存塊,則HeapReAlloc返回新地址。

 

pvMem指定要調(diào)整大小的內(nèi)存塊。

 

dwBytes指定內(nèi)存塊的新大小。

 

分配一塊內(nèi)存后,調(diào)用HeapSize可以獲得這塊內(nèi)存的實際大小:

 

 

  1. SIZE_T HeapSize(  
  2.   
  3.     HANDLE hHeap,  
  4.   
  5.     DWORD fdwFlags,  
  6.   
  7.     LPCVOID pvMem);  


 

hHeap用來標(biāo)識堆。

 

pvMem表示內(nèi)存地址。

 

dwFlags可以是0HEAP_NO_SERIALIZE

 

當(dāng)不要使用一塊內(nèi)存時可以調(diào)用HeapFree來釋放它:

 

 

  1. BOOL HeapFree(  
  2.   
  3.     HANDLE hHeap,  
  4.   
  5.     DWORD fdwFlags,  
  6.   
  7.     PVOID pvMem);  


 

如果操作成功則返回TRUE。調(diào)用這個函數(shù)可能會使堆管理器撤銷一些已經(jīng)調(diào)撥的物理存儲器。

 

如果應(yīng)用程序不再需要自己創(chuàng)建的堆,可以調(diào)用HeapDestroy來銷毀它:

 

 

  1. BOOL HeapDestroy(HANDLE hHeap);  


 

此時系統(tǒng)會收回堆所占用的物理存儲器和地址空間區(qū)域。執(zhí)行成功則返回TRUE。如果我們不調(diào)用此函數(shù)主動銷毀自己創(chuàng)建的堆,在進(jìn)程結(jié)束時,系統(tǒng)會替我們銷毀。我們不能調(diào)用此函數(shù)銷毀默認(rèn)堆,默認(rèn)堆由系統(tǒng)管理。

 

C++中使用堆

 

C++中我們可以調(diào)用new操作符來分配類對象。不需要時可以調(diào)用delete來釋放它。如

 

 

  1. CA *pCA=new CA;  


 

在編譯此段代碼時,編譯器會首先檢查類CA是否重載了new操作符成員函數(shù)。如果找到編譯器會調(diào)用這個函數(shù)。否額,會調(diào)用C++標(biāo)準(zhǔn)的new操作符。

 

 

  1. deleted pCA;  


 

對此句代碼C++編譯器會執(zhí)行與上面類似的步驟,只有CA類沒有重載delete操作符成員函數(shù)時,才會調(diào)用標(biāo)準(zhǔn)的C++delete運算符。

 

通過對C++類的newdelete操作符進(jìn)行重載,我們可以非常容易的將堆函數(shù)加以運用:

 

 

  1. class CA  
  2.   
  3. {  
  4.   
  5.   public:  
  6.   
  7.        CA();  
  8.   
  9.        ~CA();  
  10.   
  11.   public:  
  12.   
  13.        void *operator new(size_t size);  
  14.   
  15.        void*operator delete(void*p);  
  16.   
  17. };  


 

上述代碼調(diào)用operator newoperator delete是從默認(rèn)堆中分配的內(nèi)存。我們可以讓其在自己創(chuàng)建的堆中分配內(nèi)存,一般讓所有對象共享同一個堆,每個對象都創(chuàng)建一個堆為導(dǎo)致額外的性能開銷??梢圆捎糜嫈?shù)法來對堆的生存期進(jìn)行控制。

 

ToolHelp函數(shù)允許我們枚舉進(jìn)程的堆以及分配的內(nèi)存塊。它包括一下函數(shù):Heap32First,Heap32Next,Heap32ListFirstHeap32ListNext

 

由于進(jìn)程在自己的地址空間可以有多個堆,GetProcessHeaps可以讓我們得到這些堆的句柄。

 

 

  1. DWORD GetProcessHeaps(  
  2.   
  3.      DWROD dwNumHeaps,  
  4.   
  5.      PHANDLE phHeaps);  


 

phHeaps是一個數(shù)組指針。用以存儲返回的堆句柄。

 

dwNumHeaps是數(shù)組大小。

 

函數(shù)返回句柄數(shù)組個數(shù)。

 

函數(shù)所返回的句柄數(shù)組中也包括進(jìn)程的默認(rèn)堆的句柄。

 

 

  1. HANDLE hHeaps[20];  
  2.   
  3. DWORD dwHeaps=GetProcessHeaps(20,hHeaps);  


 

HeapValidate可以驗證堆的完整性。

 

 

  1. BOOL HeapValidate(  
  2.   
  3.     HANDLE hHeap,  
  4.   
  5.     DWORD fdwFlags,  
  6.   
  7.     LPCVOID pvMem);  


 

通常在調(diào)用這個函數(shù)時,我們會傳一個堆句柄和一個標(biāo)志0,并傳入NULLpvMem。該函數(shù)會遍歷堆中的各個內(nèi)存塊,確保沒有任何一塊內(nèi)存被破壞。如果給pvMem制定一塊內(nèi)存地址,那么函數(shù)就只檢查這一塊內(nèi)存的完整性。

 

為了讓堆中閑置的內(nèi)存塊能重新結(jié)合在一起,并撤銷調(diào)撥給堆中閑置內(nèi)存塊的存儲器,可以調(diào)用HeapCompact

 

  1. UINT HeapCompact(  
  2.   
  3.     HANDLE hHeap,  
  4.   
  5.     DWORD fdwFlags);  


 

一般來說會傳0fdwFlags

 

下面兩個函數(shù)要配對使用,用于線程同步:

  1. BOOL HeapLock(HANDLE hHeap);  
  2.   
  3. BOOL HeapUnlock(HANDLE hHeap);  


 

 

當(dāng)?shù)谝粋€線程調(diào)用HeapLock時,它就占有了堆。其他線程在調(diào)用堆函數(shù)時,系統(tǒng)就會暫停其他線程。只有當(dāng)?shù)谝粋€線程調(diào)用HeapUnlock之后才會喚醒被暫停的進(jìn)程。

 

HeapAlloc,HeapSizeHeapFree這些函數(shù)會在內(nèi)部調(diào)用HeapLockHeapUnlock,一般來說不需要自己去調(diào)用HeapLockHeapUnlock。

 

最后一個函數(shù)是HeapWalk,它允許我們遍歷堆的內(nèi)容。只用于調(diào)試。具體不再介紹。

            以上參考自《Windows核心編程》第五版第三部分,如有紕漏,請不吝指正??!

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多