?? 010.txt
字號(hào):
.if eax
add dwTotalMemory,1000000
inc dwCount
.endif
pop eax
invoke GlobalReAlloc,eax,100,GMEM_ZEROINIT
sub dwTotalMemory,1000000 - 100
invoke SetDlgItemInt,hWinMain,IDC_MEMORY,\
dwTotalMemory,FALSE
invoke SetDlgItemInt,hWinMain,IDC_COUNT,\
dwCount,FALSE
.until ! @lpLastMem
invoke SetDlgItemText,hWinMain,IDC_INFO,addr szInfo
mov ifCanQuit,1
ret
_ProcThread endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcDlgMain proc uses ebx edi esi hWnd,wMsg,wParam,lParam
local @dwTemp
mov eax,wMsg
.if eax == WM_CLOSE
.if ifCanQuit
invoke EndDialog,hWnd,NULL
.endif
;********************************************************************
.elseif eax == WM_INITDIALOG
push hWnd
pop hWinMain
invoke LoadIcon,hInstance,ICO_MAIN
invoke SendMessage,hWnd,WM_SETICON,ICON_BIG,eax
invoke CreateThread,NULL,0,offset _ProcThread,NULL,\
NULL,addr @dwTemp
invoke CloseHandle,eax
;********************************************************************
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
_ProcDlgMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke DialogBoxParam,hInstance,DLG_MAIN,\
NULL,offset _ProcDlgMain,NULL
invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start
對應(yīng)的資源文件Fragment.rc如下:
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#include
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#define ICO_MAIN 1000
#define DLG_MAIN 100
#define IDC_MEMORY 101
#define IDC_COUNT 102
#define IDC_INFO 103
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN ICON "Main.ico"
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DLG_MAIN DIALOG 308, 207, 130, 50
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "碎片內(nèi)存演示"
FONT 9, "宋體"
{
RTEXT "申請內(nèi)存總數(shù):", -1, 7, 8, 60, 8
EDITTEXT IDC_MEMORY, 69, 5, 55, 12,
ES_AUTOHSCROLL | ES_READONLY | WS_BORDER | WS_TABSTOP
RTEXT "申請次數(shù):", -1, 7, 21, 60, 8
EDITTEXT IDC_COUNT, 69, 19, 55, 12,
ES_AUTOHSCROLL | ES_READONLY | WS_BORDER | WS_TABSTOP
LTEXT "", IDC_INFO, 7, 37, 120, 8
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
程序在WM_INITDIALOG消息中建立了一個(gè)線程來循環(huán)申請內(nèi)存(相當(dāng)于在后臺(tái)執(zhí)行_ProcThread子程序,與多線程相關(guān)的內(nèi)容請參見第12章)。全局變量dwCount記錄了申請的次數(shù),每次申請內(nèi)存就將它的值加1。dwTotalMemory記錄了程序申請到的內(nèi)存總數(shù),每申請一個(gè)1 MB的內(nèi)存,程序?qū)⑺闹导由? 000 000,每次用GlobalReAlloc縮小內(nèi)存塊,則將它的值減去999 900。當(dāng)最后申請內(nèi)存失敗的時(shí)候,repeat循環(huán)結(jié)束。
在Windows 2000下運(yùn)行一下程序以驗(yàn)證結(jié)果,幾秒的運(yùn)行中,顯示的計(jì)數(shù)不斷增加,最后的結(jié)果如圖10.3所示。
圖10.3 內(nèi)存碎片化的演示結(jié)果
結(jié)果和預(yù)想的一樣,經(jīng)過2 027次的操作,只保留了近202 700 B的內(nèi)存,程序就成功地“謀殺”了所有的地址空間,讓整個(gè)2 GB中間充滿了碎片,以至于連1 MB大小的內(nèi)存也無法申請了!當(dāng)程序在Windows 9x中運(yùn)行時(shí),由于9x系統(tǒng)在高端和低端輪換分配內(nèi)存塊,所以同樣的辦法就不會(huì)產(chǎn)生內(nèi)存碎片,但是如果在循環(huán)中先Alloc兩次、然后Realloc兩次的話仍然可以造成內(nèi)存碎片化。
雖然這是一個(gè)極端的情況,但在現(xiàn)實(shí)中會(huì)發(fā)生嗎?會(huì)的!例如編寫一個(gè)遍歷二叉樹的程序,每增加一個(gè)結(jié)點(diǎn)的時(shí)候申請一塊內(nèi)存,用來存放指向其他結(jié)點(diǎn)的指針以及附加在結(jié)點(diǎn)上的數(shù)據(jù),當(dāng)結(jié)點(diǎn)處理完畢后縮小內(nèi)存塊,只留下指針數(shù)據(jù),那么情況就和演示程序類似,當(dāng)樹的結(jié)點(diǎn)足夠多的時(shí)候,經(jīng)過一段時(shí)間的操作,內(nèi)存中就會(huì)充滿碎片。
解決內(nèi)存碎片化的辦法很簡單,因?yàn)樗槠g有大量的內(nèi)存是空閑的,只要允許Windows移動(dòng)小塊的在用內(nèi)存,就可以將碎片合并成大塊的空閑內(nèi)存,但是在用內(nèi)存被移動(dòng)后,程序中對應(yīng)的指針也要隨著改變,不然就會(huì)訪問到錯(cuò)誤的地址,而且,在使用內(nèi)存的過程中,內(nèi)存需要有個(gè)鎖定的過程,否則用到一半的時(shí)候被Windows移動(dòng)了,結(jié)果依然是錯(cuò)誤的,只有程序?qū)?nèi)存解鎖,Windows才可以自由移動(dòng)它們,這就引伸出了可移動(dòng)內(nèi)存塊的概念和操作的基本方法。
要申請一個(gè)可移動(dòng)的內(nèi)存塊,使用的函數(shù)還是GlobalAlloc,但需要使用不同的參數(shù):
invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,dwBytes
.if eax
mov hMemory,eax
.endif
GMEM_MOVEABLE標(biāo)志指定了分配的內(nèi)存是可移動(dòng)的,GMEM_ZEROINIT同樣表示將申請到的內(nèi)存塊的內(nèi)容初始化為0(也可以用GHND標(biāo)志,它就相當(dāng)于GMEM _MOVEABLE or GMEM_ZEROINIT);如果內(nèi)存申請失敗,eax中返回NULL,成功的話返回值是一個(gè)句柄而不是內(nèi)存指針,用戶需要保存這個(gè)句柄,在鎖定或釋放內(nèi)存的時(shí)候還要用到它。一個(gè)進(jìn)程可以申請的可移動(dòng)內(nèi)存的塊數(shù)最大不能超過65 536個(gè),申請固定內(nèi)存塊時(shí)則沒有數(shù)量限制。
要使用可移動(dòng)內(nèi)存之前,需要把它鎖定,這相當(dāng)于告訴Windows現(xiàn)在程序要使用這塊內(nèi)存了,不能將它移動(dòng),鎖定內(nèi)存使用GlobalLock函數(shù):
invoke GlobalLock,hMemory
.if eax
mov lpMemory,eax
.endif
函數(shù)的入口參數(shù)是GlobalAlloc返回的內(nèi)存句柄,如果鎖定成功,函數(shù)返回一個(gè)指針,程序可以用使用固定內(nèi)存塊同樣的方法來使用它;如果鎖定失敗,則函數(shù)返回NULL。每次鎖定返回的指針位置可能是不同的,但內(nèi)存塊中的數(shù)據(jù)不會(huì)變化。
當(dāng)程序暫時(shí)不需要操作這塊內(nèi)存的時(shí)候,應(yīng)該將它解鎖,否則和使用固定的內(nèi)存塊就沒有區(qū)別了,解鎖使用GlobalUnlock函數(shù):
invoke GlobalUnlock,hMemory
函數(shù)的參數(shù)同樣是GlobalAlloc返回的句柄,解鎖成功的話函數(shù)返回非0值。讀者可能有個(gè)問題:在多線程的程序中,兩個(gè)地方同時(shí)鎖定內(nèi)存,但當(dāng)一個(gè)地方還在使用的情況下另一個(gè)地方卻調(diào)用GlobalUnlock將內(nèi)存解鎖了怎么辦?其實(shí)不用擔(dān)心這個(gè)問題,Windows為每個(gè)可移動(dòng)的內(nèi)存句柄維護(hù)一個(gè)鎖定計(jì)數(shù),每次鎖定內(nèi)存的時(shí)候計(jì)數(shù)加1,解鎖的時(shí)候計(jì)數(shù)減1,只有當(dāng)計(jì)數(shù)為0的時(shí)候內(nèi)存才真正被解鎖,所以只要程序中的GlobalLock函數(shù)和GlobalUnlock函數(shù)是配對的,就不用擔(dān)心這個(gè)問題。
要釋放一個(gè)可移動(dòng)的內(nèi)存塊,同樣使用GlobalFree函數(shù):
invoke GlobalFree,hMemory
但使用的參數(shù)是GlobalAlloc返回的內(nèi)存句柄,如果釋放成功,函數(shù)返回NULL。不管內(nèi)存當(dāng)前是否處在鎖定狀態(tài),都可以被成功釋放。
調(diào)整可移動(dòng)內(nèi)存塊的大小,同樣使用GlobalReAlloc函數(shù):
invoke GlobalReAlloc,hMemory,dwBytes,GMEM_ZEROINIT or GMEM_MOVEABLE
如果調(diào)整成功,返回值就是輸入的hMemory,失敗的話返回值是NULL。即使內(nèi)存塊在鎖定狀態(tài),函數(shù)仍然可以調(diào)用成功,但這時(shí)候內(nèi)存塊可能已經(jīng)被移動(dòng)了位置,原來用GlobalLock函數(shù)獲取的指針可能已經(jīng)失效了,所以調(diào)整可移動(dòng)內(nèi)存塊的大小最好還是先將內(nèi)存解鎖,等調(diào)整完畢以后再鎖定使用。
由于使用可移動(dòng)的內(nèi)存塊多了一個(gè)鎖定的動(dòng)作,速度自然要比使用固定的內(nèi)存塊要慢一點(diǎn),但固定內(nèi)存塊又存在碎片問題,程序中使用哪種方法有個(gè)取舍的問題。如果程序要頻繁地分配和釋放不定長的內(nèi)存塊,內(nèi)存的碎片化現(xiàn)象就比較嚴(yán)重,特別是當(dāng)程序長時(shí)間運(yùn)行時(shí),這種情況下使用可移動(dòng)內(nèi)存塊比較好;如果程序只進(jìn)行少量的內(nèi)存操作,或者雖然頻繁分配和釋放內(nèi)存,但使用的內(nèi)存塊長度都是一樣的,則使用固定內(nèi)存塊可以節(jié)省時(shí)間。
3. 可丟棄的內(nèi)存塊
分配可移動(dòng)內(nèi)存塊的時(shí)候還可以配合GMEM_MOVEABLE標(biāo)志使用GMEM_DI SCARDABLE標(biāo)志,這樣生成的內(nèi)存塊是可丟棄的內(nèi)存塊,表示當(dāng)Windows急需內(nèi)存使用的時(shí)候,可以將它從物理內(nèi)存中丟棄,可丟棄的內(nèi)存塊首先必須是可移動(dòng)的內(nèi)存塊。函數(shù)調(diào)用如下:
invoke GlobalAlloc,GHND or GMEM_DISCARDABLE,dwBytes
.if eax
mov hMemory,eax
.endif
當(dāng)用GlobalLock鎖定內(nèi)存的時(shí)候如果返回NULL指針,表示內(nèi)存已經(jīng)被Windows丟棄了,當(dāng)然其中的數(shù)據(jù)也丟失了,程序需要重新生成數(shù)據(jù)。當(dāng)內(nèi)存塊被丟棄的時(shí)候,內(nèi)存句柄還是有效的,如果程序還要使用這個(gè)句柄,那么可以對它使用GlobalReAlloc函數(shù)來重新分配內(nèi)存。
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -