VirtualAlloc 分配的內(nèi)存是以 4K 為最小單位、連續(xù)的內(nèi)存地址(但映射到真實的內(nèi)存時它不一定是連續(xù)的), 前面說了, 它不適合分配小內(nèi)存(譬如只有幾個字節(jié)的變量); 局部的變量在 "棧" 中有程序自動管理, 那么那些全局的小變量怎么辦呢? 這就要用到 "堆".
這樣看來, VirtualAlloc 分配的內(nèi)存既不是 "棧" 也不是 "堆"; VirtualAlloc 分配的內(nèi)存地址是連續(xù)的, "堆" 中內(nèi)容一般是不連續(xù)的, 所以管理 "堆" 比較麻煩, 它是通過雙線鏈表的結(jié)構(gòu)方式管理的; 程序可以擁有若干個 "堆", 每一個 "堆" 都會有一個句柄, 訪問 "堆" 中的內(nèi)容時先要找到這個 "堆", 然后再遍歷鏈表, 這可能就是 "堆" 比 "棧" 慢的根本原因.
在 "堆" 中分配內(nèi)存(HeapAlloc)前先要建立 "堆"(HeapCreate), 就像程序有默認的 "棧" 一樣, 每一個程序都有一個默認建立的 "堆"(可以用 GetProcessHeap 獲取這個 "默認堆" 的句柄), 我們在 Delphi 中用到 "堆" 時, 使用的就是這個 "默認堆". 如果讓程序更靈活地擁有多個 "堆", 必須要用到 API 函數(shù).
建立 "堆" 時會同時提交真實內(nèi)存的, 這在申請大內(nèi)存時會很慢, 所以默認堆也只有 1M, 但 "默認堆" 并沒有限制大小, 它會根據(jù)需要動態(tài)增長.
有了 "默認堆" 還有必要申請其他的 "堆" 嗎? 這只有在多線程中才能體現(xiàn)出來, 和 "棧" 不一樣, 程序會給每個線程分配一個 "棧區(qū)"; 而 "默認堆" 是進程中的所有線程公用的, 當一個線程使用 "默認堆" 時, 另一個需要使用 "堆" 的線程就要先掛起等待, 也就是它們不能同時使用; 只有通過 API 函數(shù)重新建立的私有堆才是互不干涉、最有效率的.
先了解一下 "堆" 相關(guān)的函數(shù).
//建立堆; 注意建立時指定的尺寸也是按頁大小(PageSize)對齊的, 譬如指定 15k, 實際會分配 16K.
HeapCreate(
flOptions: DWORD; {堆屬性選項, 見下表}
dwInitialSize: DWORD; {初始尺寸, 單位是字節(jié); 該大小會被直接提交到實際的內(nèi)存}
dwMaximumSize: DWORD {最大尺寸, 如果不限定最大值就設(shè)為 0}
): THandle; {返回堆句柄; 失敗返回 0, 但如果參數(shù) flOptions 允許了異常, 失敗會返回異常標識}
//flOptions 參數(shù)可選值:
HEAP_NO_SERIALIZE = 1; {非互斥, 此標記可允許多個線程同時訪問此堆}
HEAP_GENERATE_EXCEPTIONS = 4; {當建立堆出錯時, 此標記可激發(fā)一個異常并返回異常標識}
HEAP_ZERO_MEMORY = 8; {把分配的內(nèi)存初始化為 0}
//flOptions 參數(shù)指定有 HEAP_GENERATE_EXCEPTIONS 時, 可能返回的異常:
STATUS_ACCESS_VIOLATION = DWORD($C0000005); {參數(shù)錯誤}
STATUS_NO_MEMORY = DWORD($C0000017); {內(nèi)存不足}
//銷毀堆
HeapDestroy(
hHeap: THandle {堆句柄}
): BOOL; {}
//從堆中申請內(nèi)存
HeapAlloc(
hHeap: THandle; {堆句柄}
dwFlags: DWORD; {內(nèi)存屬性選項, 見下表}
dwBytes: DWORD {申請內(nèi)存的大小, 單位是字節(jié)}
): Pointer; {返回內(nèi)存指針; 失敗返回 0 或異常, 情況和建立堆是一樣}
//dwFlags 參數(shù)可選值:
HEAP_NO_SERIALIZE = 1; {非互斥, 此標記可允許多個線程同時訪問此堆}
HEAP_GENERATE_EXCEPTIONS = 4; {當建立堆出錯時, 此標記可激發(fā)一個異常并返回異常標識}
HEAP_ZERO_MEMORY = 8; {把分配的內(nèi)存初始化為 0}
{能看出這和堆的屬性選項是一樣的; 如果 dwFlags 參數(shù)設(shè)為 0, 將使用堆的屬性; 如果重新指定將覆蓋堆的屬性}
{另外: 如果堆是默認堆, 也就是堆句柄來自 GetProcessHeap, dwFlags 參數(shù)會被忽略}
//改變堆內(nèi)存的大小, 也就是重新分配
HeapReAlloc(
hHeap: THandle; {句柄}
dwFlags: DWORD; {內(nèi)存屬性選項; 該參數(shù)比 HeapAlloc 多出一個選項, 見下表}
lpMem: Pointer; {原內(nèi)存指針}
dwBytes: DWORD {新的尺寸}
): Pointer; {同 HeapAlloc}
//dwFlags 參數(shù)可選值:
HEAP_NO_SERIALIZE = 1; {非互斥, 此標記可允許多個線程同時訪問此堆}
HEAP_GENERATE_EXCEPTIONS = 4; {當建立堆出錯時, 此標記可激發(fā)一個異常并返回異常標識}
HEAP_ZERO_MEMORY = 8; {把分配的內(nèi)存初始化為 0}
HEAP_REALLOC_IN_PLACE_ONLY = 16; {此標記不允許改變原來的內(nèi)存位置}
//獲取堆中某塊內(nèi)存的大小
HeapSize(
hHeap: THandle; {堆句柄}
dwFlags: DWORD; {內(nèi)存屬性; 可選值是 0 或 HEAP_NO_SERIALIZE, 后者可確保同步訪問}
lpMem: Pointer {內(nèi)存指針}
): DWORD; {成功返回字節(jié)為單位的大小; 失敗返回 $FFFFFFFF}
//釋放堆中指定的內(nèi)存塊
HeapFree(
hHeap: THandle; {堆句柄}
dwFlags: DWORD; {內(nèi)存屬性; 可選值是 0 或 HEAP_NO_SERIALIZE}
lpMem: Pointer {內(nèi)存指針}
): BOOL; {}
//驗證堆
HeapValidate(
hHeap: THandle; {}
dwFlags: DWORD; {}
lpMem: Pointer {}
): BOOL; {}
//整理堆
HeapCompact(
hHeap: THandle; {}
dwFlags: DWORD {}
): UINT; {}
//鎖定堆
HeapLock(
hHeap: THandle {}
): BOOL; {}
//鎖定后的解鎖
HeapUnlock(
hHeap: THandle {}
): BOOL; {}
//列舉堆中的內(nèi)存塊
HeapWalk(
hHeap: THandle; {}
var lpEntry: TProcessHeapEntry {}
): BOOL; {}
舉例放下篇吧.