Windows内存管理机制及C++内存分配实例五堆Word文档下载推荐.docx
- 文档编号:19466126
- 上传时间:2023-01-06
- 格式:DOCX
- 页数:14
- 大小:103.21KB
Windows内存管理机制及C++内存分配实例五堆Word文档下载推荐.docx
《Windows内存管理机制及C++内存分配实例五堆Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《Windows内存管理机制及C++内存分配实例五堆Word文档下载推荐.docx(14页珍藏版)》请在冰豆网上搜索。
第一个值是堆的保留空间,第二个值是堆开始时提交的物理内存大小。
本文将堆改变为100M。
当你在程序中扩大了堆提交的物理内存时,进程运行时,物理内存将减少扩大的数量。
但是,默认堆总是可以扩大的,不能限制它的最大值。
当你在程序中扩大了堆保留的空间时,进程运行时,可用进程空间将会减少扩大的数量。
每次你用New操作符分配内存时,进程空间会相应的减少,物理内存也会相应的减少。
一个重要的提示,本文经过测试,如果你需要的内存块大部分都超过512K,那么,建堆时给它的初始大小不应该很大,因为,如果你所需内存块大于512K的话,它不是从堆中分配的,也就是说不用堆中默认的空间,但其仍然属于堆管理。
默认堆的一个用处是系统函数需要利用它运行。
比如,Windows2000的字符集是UNICODE的,如果调用ANSI版本的函数,系统需要利用堆来从ANSI到UNICODE的转换,调用UNICODE版本的函数。
自建堆
ü
使用场合
保护数据结构:
将不同的数据结构存在不同的堆中,可以防止不同的结构之间由于指针误操作而破坏了它们。
消除内存碎片:
将大小不同的结构保存在一个堆中,会导致碎片的产生,比如释放一个小结构时,大结构也不能利用它。
独享堆的快速:
如果用默认堆的话,线程之间是同步访问,速度慢;
如果创建独享堆,则系统可以不需同步,比较快。
第二个快速体现在释放的快速,默认堆中,你只能释放某个内存块,而不能释放整个堆;
而独享堆可以一次释放堆,也就是释放了所有的内存块。
开始使用
建立堆:
使用以下API
HANDLEHeapCreate(DWORD选项,SIZE_T初始大小,SIZE_T最大值)
“选项”取值为0,不是以下任意一个
HEAP_NO_SERIALIZE,系统无需同步堆
HEAP_GENERATE_EXCEPTIONS,当创建失败或分配失败时产生异常。
“初始大小”是堆的大小,系统会规整到页面的整数倍,如0~4096的任何数都为4096;
但是,进程空间至少要64K。
“最大值”是堆允许的最大值;
为0则无限。
使用HEAP_NO_SERIALIZE需确定只有单线程访问这个堆,否则有可能破坏堆;
或程序有同步代码来同步堆。
C++程序如下:
pHeap=(char*)GetProcessHeap();
printf("
默认堆地址=%x\n"
pHeap);
MEMORYSTATUSmemStatus2;
GlobalMemoryStatus(&
memStatus2);
HANDLEhHeap=HeapCreate(HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS,1024*1024*50,0);
char*pHeap=(char*)hHeap;
printf("
新建堆1地址=%x\n"
if(hHeap==NULL)
{
cout<
<
"
创建堆失败!
endl;
}
MEMORYSTATUSmemStatus3;
memStatus3);
建立堆后:
cout<
减少物理内存="
memStatus2.dwAvailPhys-memStatus3.dwAvailPhys<
减少可用页文件="
memStatus2.dwAvailPageFile-memStatus3.dwAvailPageFile<
减少可用进程空间="
memStatus2.dwAvailVirtual-memStatus3.dwAvailVirtual<
endl<
HANDLEhHeap2=HeapCreate(HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS,1024*1024*10,0);
char*pHeap2=(char*)hHeap2;
新建堆2地址=%x\n"
pHeap2);
结果如下:
当建立堆1时,它分配了50M的物理内存给堆使用;
当建立堆2时,堆2的地址是0x04bc0000=0x019c0000+50*1024*1024.
分配内存:
使用以下API
PVOIDHeapAlloc(HANDLE堆句柄,DWORD选项,SIZE_T字节数)
“选项”可以是,
HEAP_ZERO_MEMORY,所有字节初始化为0
HEAP_NO_SERIALIZE,堆这个内存区独享
HEAP_GENERATE_EXCEPTIONS,产生异常。
如果创建堆有了它就不用再设了。
异常可能为:
STATUS_NO_MEMOR(无足够内存)和STATUS_ACCESS_VIOLATION(堆被破坏,分配失败)。
GlobalMemoryStatus(&
PVOIDpV=HeapAlloc(hHeap,
HEAP_ZERO_MEMORY|HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS,1024*507);
if(pV==NULL)
分配堆内存失败!
char*pC=(char*)pV;
第一次分配地址=%x\n"
pC);
MEMORYSTATUSmemStatus4;
memStatus4);
第一次堆分配后:
memStatus3.dwAvailPhys-memStatus4.dwAvailPhys<
memStatus3.dwAvailPageFile-memStatus4.dwAvailPageFile<
memStatus3.dwAvailVirtual-memStatus4.dwAvailVirtual<
PVOIDpV2=HeapAlloc(hHeap,
HEAP_ZERO_MEMORY|HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS,1024*508);
if(pV2==NULL)
char*pC2=(char*)pV2;
第二次分配地址=%x\n"
pC2);
MEMORYSTATUSmemStatus5;
memStatus5);
第二次堆分配后:
memStatus4.dwAvailPhys-memStatus5.dwAvailPhys<
memStatus4.dwAvailPageFile-memStatus5.dwAvailPageFile<
memStatus4.dwAvailVirtual-memStatus5.dwAvailVirtual<
for(inti=0;
i<
200*1024;
i++)
pC2[i]=9;
MEMORYSTATUSmemStatus10;
memStatus10);
第二次堆使用一半后:
memStatus5.dwAvailPhys-memStatus10.dwAvailPhys<
memStatus5.dwAvailPageFile-memStatus10.dwAvailPageFile<
memStatus5.dwAvailVirtual-memStatus10.dwAvailVirtual<
结果如下:
可以看出,第一次分配507K的地址为0x04add650<
0x04bc0000,它是在堆中分配的;
第二次分配508K的地址为0x055c0020>
0x04bc0000,它是在堆外分配的;
无论在多大的堆中,只要分配内存块大于507K时,都会在堆外分配,但是,它像在堆中一样,存在堆的链接表中,受堆管理。
分配时,系统使用的是虚拟页文件;
只有在真正使用时,才会分配物理内存。
至于为什么分配大于507K会在堆外分配而不直接使用堆中的内存,目前仍然不清楚。
改变大小:
PVOIDHeapReAlloc(HANDLE堆句柄,DWORD选项,PVOID旧内存块地址,SIZE_T新内存块大小)
“选项”除了以上三个外,还有HEAP_REALLOC_IN_PLACE_ONLY,指定不能移动原有内存块的地址。
PVOIDpV2New=HeapReAlloc(hHeap,0,pV2,1024*1024*2);
if(pV2New!
=NULL)
char*pC2New=(char*)pV2New;
改变分配地址=%x\n"
pC2New);
pC2New[0]<
//cout<
pC2[0]<
出现访问违规
SIZE_TlenNew=HeapSize(hHeap,0,pV2New);
改变后大小="
lenNew<
改变分配后:
可以看出,新内存块紧接着原来内存块结束的地方开始创建,大小为2M;
原来的内存块的内容被销毁和释放,所以新内存块只减少了增加的内存量。
一个缺点就是,新内存块居然不保留原来内存的内容!
另外,如果采用HEAP_REALLOC_IN_PLACE_ONLY的话,出现NotEnoughQuote异常。
也就是说,当前内存的状况是,必须移动才可以扩大此内存块。
查询内存:
可以查询堆中一个内存块的大小。
SIZE_THeapSize(HANDLE堆句柄,DWORD选项,LPVOID内存块地址)
“选项”可为0或HEAP_NO_SERIALIZE。
参考以上例子。
释放内存块:
BOOLHeapFree(HANDLE堆句柄,DWORD选项,PVOID内存块地址)
C++程序如下:
HeapFree(hHeap,0,pV2New);
MEMORYSTATUSmemStatus6;
memStatus6);
第二次堆分配释放后:
增加物理内存="
memStatus6.dwAvailPhys-memStatus5.dwAvailPhys<
增加可用页文件="
memStatus6.dwAvailPageFile-memStatus5.dwAvailPageFile<
增加可用进程空间="
memStatus6.dwAvailVirtual-memStatus5.dwAvailVirtual<
内存空间释放了原来的2M空间。
释放堆:
BOOLHeapDestroy(HANDLE堆句柄)
不能用它释放默认堆,系统忽略它的处理。
这一次,我们先在堆1中分配了70M的内存,由于它很大,所以,堆在堆外给它分配了内存,所以,堆1一共有50M+70M=120M。
释放程序如下:
PVOIDpV4=HeapAlloc(hHeap,HEAP_ZERO_MEMORY|HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS)
1024*1024*70);
if(pV4==NULL)
char*pC4=(char*)pV4;
第四次堆分配=%x\n"
pC4);
MEMORYSTATUSmemStatus9;
memStatus9);
分配堆内存后:
memStatus7.dwAvailPhys-memStatus9.dwAvailPhys<
memStatus7.dwAvailPageFile-memStatus9.dwAvailPageFile<
memStatus7.dwAvailVirtual-memStatus9.dwAvailVirtual<
SIZE_Tlen=HeapSize(hHeap,0,pV4);
len="
len<
boolre=HeapDestroy(hHeap);
if(re==false)
释放堆失败!
}
MEMORYSTATUSmemStatus8;
memStatus8);
释放堆后:
memStatus8.dwAvailPhys-memStatus9.dwAvailPhys<
memStatus8.dwAvailPageFile-memStatus9.dwAvailPageFile<
memStatus8.dwAvailVirtual-memStatus9.dwAvailVirtual<
如所猜想一样,释放了120M内存。
获取所有堆:
DWORDGetProcessHeaps(DWORD数量,PHANDLE句柄数组)
“数量”是你想获取的堆数目;
“句柄数组”是获得的堆句柄。
默认堆也可以获取。
HANDLEhandles[10];
memset(handles,0,sizeof(handles));
GetProcessHeaps(10,handles);
10;
堆"
i+1<
="
handles[i]<
可以看见,一共有8个堆,堆1是默认堆,堆7和堆8是本文建立的堆。
另外5个不知来源。
验证堆:
BOOLHeapValidate(HANDLE堆句柄,DWORD选项,LPVOID内存块地址)
“选项”可为0或HEAP_NO_SERIALIZE;
“内存块地址”为NULL时,验证所有内存块。
HANDLEhandles[10];
"
;
if(HeapValidate(handles[i],0,NULL))
验证堆成功!
else
合并内存块:
UINTHeapCompact(HANDLE堆句柄,DWORD选项)
此函数可以合并空闲内存块。
其他函数:
HeapLock和HeapUnlock通常是系统使用的;
HeapWalk可以遍历堆内存,需要以上两个函数。
C++内存函数
Malloc和Free
这是C语言使用的函数,只能从默认堆中分配内存,并且只是分配内存,不能调用构造函数,且只是按字节分配,不能按类型分配。
New和Delete
这是C++语言使用的函数,默认情况下从默认堆中分配内存,但是也可以通过重载New函数,从自建堆中按类型分配;
同时可以执行构造函数和析构函数。
它底层是通过HeapAlloc和HeapFree实现的。
依赖于编译器的实现。
GlobalAlloc和GlobalFree
这是比HeapAlloc和HeapFree更慢的函数,但是也没有比它们更好的优点,只能在默认堆中分配;
16位操作系统下利用它们分配内存。
LocalAlloc和LocalFree
在WindowsNT内核里,和GlobalAlloc、GlobalFree是一样的。
一个例子
默认情况下,New关键字是利用HeapAlloc在默认堆上建立对象。
本文重载了类的New方法,使得类在自己的堆中存放,这样可以与外面的对象隔离,以免重要的数据结构被意外破坏。
由于类中的成员变量是在堆中存放,因此不局限于线程堆栈的1M空间。
classAllocateInOtherHeap
{
public:
AllocateInOtherHeap(void);
~AllocateInOtherHeap(void);
void*operatornew(size_tsize);
staticHANDLEheap;
public:
//类对象唯一所需的空间
intiArray[1024*1024*10];
AllocateInOtherHeap:
:
AllocateInOtherHeap(void)
AllocateInOtherHeap()"
//如果New函数没有分配够空间,那么此处会出现访问违规
memset(iArray,0,sizeof(AllocateInOtherHeap));
iArray[1024]=8;
void*AllocateInOtherHeap:
operatornew(size_tsize)
{
if(heap==NULL)
heap=HeapCreate(HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS,1024*1024*10,0);
//分配足够这个类对象的空间
void*p=HeapAlloc(heap,0,sizeof(AllocateInOtherHeap));
堆的大小="
HeapSize(heap,0,p)<
AllocateInOtherHeap堆地址=%x\n"
heap);
AllocateInOtherHeap返回地址=%x\n"
p);
returnp;
AllocateInOtherHeap:
~AllocateInOtherHeap(void)
~AllocateInOtherHeap"
voidAllocateInOtherHeap:
operatordelete(void*p)
HeapFree(heap,0,p);
HeapDestroy(heap);
delete()"
};
可见,new函数先分配够空间,然后才能初始化对象变量;
而delete函数得先做析构,才能释放空间。
对象保存在堆外,因为大于512K;
对象大小刚好是iArray变量的大小。
注意,如果没有分配足够的空间,虽然你可以得到对象指针,但是你访问数据时可能会出现访问违规,如果没出现,那更惨,意味着你读写了别人的数据。
本文来自CSDN博客,转载自:
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Windows 内存 管理机制 C+ 分配 实例