B)分配内存:
使用以下API
PVOID HeapAlloc(HANDLE 堆句柄,DWORD 选项,SIZE_T 字节数)
“选项”可以是,
HEAP_ZERO_MEMORY,所有字节初始化为0
HEAP_NO_SERIALIZE,堆这个内存区独享
HEAP_GENERATE_EXCEPTIONS,产生异常。如果创建堆有了它就不用再设了。异常可能为:STATUS_NO_MEMOR(无足够内存)和STATUS_ACCESS_VIOLATION(堆被破坏,分配失败)。
C++程序如下:
GlobalMemoryStatus(&memStatus3);
PVOID pV=HeapAlloc(hHeap,
HEAP_ZERO_MEMORY|HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS,1024*507);
if(pV==NULL)
{
cout<<"分配堆内存失败!"<<endl;
}
char * pC=(char*)pV;
printf("第一次分配地址=%x\n",pC);
MEMORYSTATUS memStatus4;
GlobalMemoryStatus(&memStatus4);
cout<<"第一次堆分配后:"<<endl;
cout<<"减少物理内存="<<memStatus3.dwAvailPhys-memStatus4.dwAvailPhys<<endl;
cout<<"减少可用页文件="<<memStatus3.dwAvailPageFile-memStatus4.dwAvailPageFile<<endl;
cout<<"减少可用进程空间="<<memStatus3.dwAvailVirtual-memStatus4.dwAvailVirtual<<endl<<endl;
PVOID pV2=HeapAlloc(hHeap,
HEAP_ZERO_MEMORY|HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS,1024*508);
if(pV2==NULL)
{
cout<<"分配堆内存失败!"<<endl;
}
char * pC2=(char*)pV2;
printf("第二次分配地址=%x\n",pC2);
MEMORYSTATUS memStatus5;
GlobalMemoryStatus(&memStatus5);
cout<<"第二次堆分配后:"<<endl;
cout<<"减少物理内存="<<memStatus4.dwAvailPhys-memStatus5.dwAvailPhys<<endl;
cout<<"减少可用页文件="<<memStatus4.dwAvailPageFile-memStatus5.dwAvailPageFile<<endl;
cout<<"减少可用进程空间="<<memStatus4.dwAvailVirtual-memStatus5.dwAvailVirtual<<endl<<endl;
for(int i=0;i<200*1024;i++)
pC2[i]=9;
MEMORYSTATUS memStatus10;
GlobalMemoryStatus(&memStatus10);
cout<<"第二次堆使用一半后:"<<endl;
cout<<"减少物理内存="<<memStatus5.dwAvailPhys-memStatus10.dwAvailPhys<<endl;
cout<<"减少可用页文件="<<memStatus5.dwAvailPageFile-memStatus10.dwAvailPageFile<<endl;
cout<<"减少可用进程空间="
<<memStatus5.dwAvailVirtual-memStatus10.dwAvailVirtual<<endl<<endl;
结果如下:
可以看出,第一次分配507K的地址为0x04ad d650<0x04bc 0000,它是在堆中分配的;第二次分配508K的地址为0x055c 0020>0x04bc 0000,它是在堆外分配的;无论在多大的堆中,只要分配内存块大于507K时,都会在堆外分配,但是,它像在堆中一样,存在堆的链接表中,受堆管理。分配时,系统使用的是虚拟页文件;只有在真正使用时,才会分配物理内存。
至于为什么分配大于507K会在堆外分配而不直接使用堆中的内存,目前仍然不清楚。
C)
改变大小:
PVOID HeapReAlloc(HANDLE 堆句柄,DWORD 选项,PVOID 旧内存块地址,SIZE_T 新内存块大小)
“选项”除了以上三个外,还有HEAP_REALLOC_IN_PLACE_ONLY,指定不能移动原有内存块的地址。
C++程序如下:
GlobalMemoryStatus(&memStatus4);
PVOID pV2New=HeapReAlloc(hHeap,0,pV2,1024*1024*2);
if(pV2New!=NULL)
{
char * pC2New=(char*)pV2New;
printf("改变分配地址=%x\n",pC2New);
cout<<pC2New[0]<<endl;
//cout<<pC2[0]<<endl;出现访问违规
SIZE_T lenNew=HeapSize(hHeap,0,pV2New);
cout<<"改变后大小="<<lenNew<<endl;
}
GlobalMemoryStatus(&memStatus5);
cout<<"改变分配后:"<<endl;
cout<<"减少物理内存="<<memStatus4.dwAvailPhys-memStatus5.dwAvailPhys<<endl;
cout<<"减少可用页文件="<<memStatus4.dwAvailPageFile-memStatus5.dwAvailPageFile<<endl;
cout<<"减少可用进程空间="
<<memStatus4.dwAvailVirtual-memStatus5.dwAvailVirtual<<endl<<endl;
结果如下:
可以看出,新内存块紧接着原来内存块结束的地方开始创建,大小为2M;原来的内存块的内容被销毁和释放,所以新内存块只减少了增加的内存量。一个缺点就是,新内存块居然不保留原来内存的内容!另外,如果采用HEAP_REALLOC_IN_PLACE_ONLY的话,出现Not Enough Quote异常。也就是说,当前内存的状况是,必须移动才可以扩大此内存块。
查询内存:
可以查询堆中一个内存块的大小。
SIZE_T HeapSize(HANDLE 堆句柄,DWORD 选项,LPVOID 内存块地址)
“选项”可为0或HEAP_NO_SERIALIZE。
参考以上例子。
释放内存块:
BOOL HeapFree(HANDLE 堆句柄,DWORD 选项,PVOID 内存块地址)
“选项”可为0或HEAP_NO_SERIALIZE。
C++程序如下:
GlobalMemoryStatus(&memStatus5);
HeapFree(hHeap,0,pV2New);
MEMORYSTATUS memStatus6;
GlobalMemoryStatus(&memStatus6);
cout<<"第二次堆分配释放后:"<<endl;
cout<<"增加物理内存="<<memStatus6.dwAvailPhys-memStatus5.dwAvailPhys<<endl;
cout<<"增加可用页文件="<<memStatus6.dwAvailPageFile-memStatus5.dwAvailPageFile<<endl;
cout<<"增加可用进程空间="<<memStatus6.dwAvailVirtual-memStatus5.dwAvailVirtual<<endl<<endl;
结果如下:
内存空间释放了原来的2M空间。
D)
释放堆:
BOOL HeapDestroy(HANDLE 堆句柄)
不能用它释放默认堆,系统忽略它的处理。
这一次,我们先在堆1中分配了70M的内存,由于它很大,所以,堆在堆外给它分配了内存,所以,堆1一共有50M+70M=120M。释放程序如下:
PVOID pV4=HeapAlloc(hHeap,HEAP_ZERO_MEMORY|HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS)
,1024*1024*70);
if(pV4==NULL)
{
cout<<"分配堆内存失败!"<<endl;
}
char * pC4=(char*)pV4;
printf("第四次堆分配=%x\n",pC4);
MEMORYSTATUS memStatus9;
GlobalMemoryStatus(&memStatus9);
cout<<"分配堆内存后:"<<endl;
cout<<"减少物理内存="<<memStatus7.dwAvailPhys-memStatus9.dwAvailPhys<<endl;
cout<<"减少可用页文件="<<memStatus7.dwAvailPageFile-memStatus9.dwAvailPageFile<<endl;
cout<<"减少可用进程空间="<<memStatus7.dwAvailVirtual-memStatus9.dwAvailVirtual<<endl<<endl;
SIZE_T len=HeapSize(hHeap,0,pV4);
cout<<"len="<<len<<endl;
bool re=HeapDestroy(hHeap);
if(re==false)
{
cout<<"释放堆失败!"<<endl;
}
MEMORYSTATUS memStatus8;
GlobalMemoryStatus(&memStatus8);
cout<<"释放堆后:"<<endl;
cout<<"增加物理内存="<<memStatus8.dwAvailPhys-memStatus9.dwAvailPhys<<endl;
cout<<"增加可用页文件="<<memStatus8.dwAvailPageFile-memStatus9.dwAvailPageFile<<endl;
cout<<"增加可用进程空间="<<memStatus8.dwAvailVirtual-memStatus9.dwAvailVirtual<<endl<<endl;
结果如下:
如所猜想一样,释放了120M内存。
E)
获取所有堆:
DWORD GetProcessHeaps(DWORD 数量,PHANDLE 句柄数组)
“数量”是你想获取的堆数目;
“句柄数组”是获得的堆句柄。
默认堆也可以获取。
HANDLE handles[10];
memset(handles,0,sizeof(handles));
GetProcessHeaps(10,handles);
for(int i=0;i<10;i++)
cout<<"堆"<<i+1<<"="<<handles[i]<<endl;
结果如下:
可以看见,一共有8个堆,堆1是默认堆,堆7和堆8是本文建立的堆。另外5个不知来源。
验证堆:
BOOL HeapValidate(HANDLE 堆句柄,DWORD 选项,LPVOID 内存块地址)
“选项” 可为0或HEAP_NO_SERIALIZE;
“内存块地址”为NULL时,验证所有内存块。
C++程序如下:
HANDLE handles[10];
memset(handles,0,sizeof(handles));
GetProcessHeaps(10,handles);
for(int i=0;i<10;i++)
{
cout<<"堆"<<i+1<<"="<<handles[i]<<" ";
if(HeapValidate(handles[i],0,NULL))
cout<<"验证堆成功!"<<endl;
else
cout<<endl;
}
结果如下:
合并内存块:
UINT HeapCompact(HANDLE 堆句柄,DWORD 选项)
“选项” 可为0或HEAP_NO_SERIALIZE;
此函数可以合并空闲内存块。
其他函数:
HeapLock和HeapUnlock 通常是系统使用的;
HeapWalk可以遍历堆内存,需要以上两个函数。