按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
add dwTotalMemory;1000000
。repeat
push @lpLastMem
invoke GlobalAlloc;GPTR;1000000
mov @lpLastMem;eax
。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
来源:电子工业出版社 作者:罗云彬 上一页 回书目 下一页
上一页 回书目 下一页
第10章 内存管理和文件操作
10。1 内 存 管 理(4)
对应的资源文件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 〃碎片内存演示〃
FONT 9; 〃宋体〃
{
RTEXT 〃申请内存总数:〃; …1; 7; 8; 60; 8
EDITTEXT IDC_MEMORY; 69; 5; 55; 12;
ES_AUTOHSCROLL | ES_READONLY | WS_BORDER | WS_TABSTOP
RTEXT 〃申请次数:〃; …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消息中建立了一个线程来循环申请内存(相当于在后台执行_ProcThread子程序,与多线程相关的内容请参见第12章)。全局变量dwCount记录了申请的次数,每次申请内存就将它的值加1。dwTotalMemory记录了程序申请到的内存总数,每申请一个1 MB的内存,程序将它的值加上1 000 000,每次用GlobalReAlloc缩小内存块,则将它的值减去999 900。当最后申请内存失败的时候,repeat循环结束。
在Windows 2000下运行一下程序以验证结果,几秒的运行中,显示的计数不断增加,最后的结果如图10。3所示。
图10。3 内存碎片化的演示结果
结果和预想的一样,经过2 027次的操作,只保留了近202 700 B的内存,程序就成功地“谋杀”了所有的地址空间,让整个2 GB中间充满了碎片,以至于连1 MB大小的内存也无法申请了!当程序在Windows 9x中运行时,由于9x系统在高端和低端轮换分配内存块,所以同样的办法就不会产生内存碎片,但是如果在循环中先Alloc两次、然后Realloc两次的话仍然可以造成内存碎片化。
虽然这是一个极端的情况,但在现实中会发生吗?会的!例如编写一个遍历二叉树的程序,每增加一个结点的时候申请一块内存,用来存放指向其他结点的指针以及附加在结点上的数据,当结点处理完毕后缩小内存块,只留下指针数据,那么情况就和演示程序类似,当树的结点足够多的时候,经过一段时间的操作,内存中就会充满碎片。
解决内存碎片化的办法很简单,因为碎片之间有大量的内存是空闲的,只要允许Windows移动小块的在用内存,就可以将碎片合并成大块的空闲内存,但是在用内存被移动后,程序中对应的指针也要随着改变,不然就会访问到错误的地址,而且,在使用内存的过程中,内存需要有个锁定的过程,否则用到一半的时候被Windows移动了,结果依然是错误的,只有程序将内存解锁,Windows才可以自由移动它们,这就引伸出了可移动内存块的概念和操作的基本方法。
要申请一个可移动的内存块,使用的函数还是GlobalAlloc,但需要使用不同的参数:
invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,dwBytes
。if eax
mov hMemory,eax
。endif
GMEM_MOVEABLE标志指定了分配的内存是可移动的,GMEM_ZEROINIT同样表示将申请到的内存块的内容初始化为0(也可以用GHND标志,它就相当于GMEM _MOVEABLE or GMEM_ZEROINIT);如果内存申请失败,eax中返回NULL,成功的话返回值是一个句柄而不是内存指针,用户需要保存这个句柄,在锁定或释放内存的时候还要用到它。一个进程可以申请的可移动内存的块数最大不能超过65 536个,申请固定内存块时则没有数量限制。
要使用可移动内存之前,需要把它锁定,这相当于告诉Windows现在程序要使用这块内存了,不能将它移动,锁定内存使用GlobalLock函数:
invoke GlobalLock;hMemory
。if eax
mov lpMemory,eax
。endif
函数的入口参数是GlobalAlloc返回的内存句柄,如果锁定成功,函数返回一个指针,程序可以用使用固定内存块同样的方法来使用它;如果锁定失败,则函数返回NULL。每次锁定返回的指针位置可能是不同的,但内存块中的数据不会变化。
当程序暂时不需要操作这块内存的时候,应该将它解锁,否则和使用固定的内存块就没有区别了,解锁使用GlobalUnlock函数:
invoke GlobalUnlock;hMemory
函数的参数同样是GlobalAlloc返回的句柄,解锁成功的话函数返回非0值。读者可能有个问题:在多线程的程序中,两个地方同时锁定内存,但当一个地方还在使用的情况下另一个地方却调用GlobalUnlock将内存解锁了怎么办?其实不用担心这个问题,Windows为每个可移动的内存句柄维护一个锁定计数,每次锁定内存的时候计数加1,解锁的时候计数减1,只有当计数为0的时候内存才真正被解锁,所以只要程序中的GlobalLock函数和GlobalUnlock函数是配对的,就不用担心这个问题。
要释放一个可移动的内存块,同样使用GlobalFree函数:
invoke GlobalFree,hMemory
但使用的参数是GlobalAlloc返回的内存句柄,如果释放成功,函数返回NULL。不管内存当前是否处在锁定状态,都可以被成功释放。
调整可移动内存块的大小,同样使用GlobalReAlloc函数:
invoke GlobalReAlloc,hMemory,dwBytes,GMEM_ZEROINIT or GMEM_MOVEABLE
如果调整成功,返回值就是输入的hMemory,失败的话返回值是NULL。即使内存块在锁定状态,函数仍然可以调用成功,但这时候内存块可能已经被移动了位置,原来用GlobalLock函数获取的指针可能已经失效了,所以调整可移动内存块的大小最好还是先将内存解锁,等调整完毕以后再锁定使用。
由于使用可移动的内存块多了一个锁定的动作,速度自然要比使用固定的内存块要慢一点,但固定内存块又存在碎片问题,程序中使用哪种方法有个取舍的问题。如果程序要频繁地分配和释放不定长的内存块,内存的碎片化现象就比较严重,特别是当程序长时间运行时,这种情况下使用可移动内存块比较好;如果程�