前言
进程堆是进程非常重要的内存区域,它里面含有了进程中分配的变量信息等,一个进程被创建时会自动创建一个默认堆,进程默认堆非常重要,系统要求进程中的线程依次访问默认堆,意味如果一个线程要对默认堆分配内存,那么其他线程一定要处于等待状态。一个进程在运行时 会创建多个堆,这些堆可以创建,也可以销毁。调用GetProcessHeap可以获取默认堆
HANDLE GetProcessHeap();
堆的创建
进程运行时产生的数据不能都放在默认堆中,因此需要创建额外的堆来存放信息,创建堆的函数如下:
HANDLE HeapCreate(
DWORD flOptions ,
DWORD dwInitialSize ,
DWORD dwMaxmumSize);
第一个参数flOptions 表明对堆的操作,取证范围是:0、HEAP_NO_SERIALIAZE、HEAP_CREATE_ENABLE_EXECUTE、HEAP_GENERATE_EXCEPTIONS
取值 | 含义 |
---|---|
HEAP_NO_SERIALIAZE | 表明线程对堆的操作是非互斥的,不需要依次访问创建的堆、但是这个标志不能应用在低碎片化堆中 |
HEAP_CREATE_ENABLE_EXECUTE | 从该堆分配的所有内存块均允许执行代码 |
HEAP_GENERATE_EXCEPTIONS | 系统引发异常以指示对HeapAlloc和HeapReAlloc的调用失败(例如,内存不足的情况),而不是返回NULL。 |
第二个参数是设置分配堆的内存初始化大小
第三个参数是设置堆的最大大小,如果值为0的话表明堆的内存大小没有限制,可以不断增长。
块的创建
创建了堆之后,就需要创建内存块来存放信息,系统提供了创建内存块的函数,下面是创建块的函数
DECLSPEC_ALLOCATOR LPVOID HeapAlloc(
HANDLE hHeap,
DWORD dwFlags,
SIZE_T dwBytes
);
第一个参数hHeap表明,在哪个堆中分配内存块;
第二个参数表明堆分配选项,指定这些值中的任何一个都将覆盖使用HeapCreate创建堆时指定的相应值。 此参数可以是以下一个或多个值。
取值 | 含义 |
---|---|
HEAP_GENERATE_EXCEPTIONS | 系统将引发异常以指示功能故障(例如内存不足情况),而不是返回NULL。 |
HEAP_NO_SERIALIZE | 线程不依次访问内存块 |
HEAP_ZERO_MEMORY | 分配的内存将初始化为零, 否则,内存不会初始化为零。 |
第三个参数表明分配的字节数
调整块的大小
块创建之后,大小可以重调,为了不浪费内存可以调小,为了能够使用到足够的内存,可以调大,重调函数如下:
DECLSPEC_ALLOCATOR LPVOID HeapReAlloc(
HANDLE hHeap,
DWORD dwFlags,
_Frees_ptr_opt_ LPVOID lpMem,
SIZE_T dwBytes
);
第一个参数hHeap表明,在哪个堆中的内存块需要重调;
第二次参数是堆重新分配选项。 使用HeapCreate函数创建堆时,指定值将覆盖flOptions参数中指定的相应值。取值情况如下:
取值 | 含义 |
---|---|
HEAP_GENERATE_EXCEPTIONS | 操作系统引发异常以指示功能故障(例如内存不足情况),而不是返回NULL。 |
HEAP_NO_SERIALIZE | 同上 |
HEAP_REALLOC_IN_PLACE_ONLY | 重新分配内存块时不能移动。 如果未指定该值,则该功能可能会将块移动到新位置。 如果指定了该值并且无法移动就无法调整该块的大小,则该功能将失败,从而使原始存储块保持不变。 |
HEAP_ZERO_MEMORY | 如果重新分配请求用于更大的大小,则超出原始大小的其他内存区域将初始化为零。 直到其原始大小的存储块的内容均不受影响。 |
第三个参数是指向该函数重新分配的内存块的指针。 该指针是通过较早调用HeapAlloc或HeapReAlloc函数返回的。
第四个参数是重新分配块的大小
销毁堆
堆既然能被创建就能被销毁,当程序不再需要自己的堆时,程序可以自动销毁它,但程序不主动销毁它的话,系统只能等进程被终止了,系统才自动销毁它,销毁堆的函数如下:
BOOL HeapDestroy(
HANDLE hHeap
);
成功销毁则返回true,否则返回false
堆的使用
我们平时编程的时候,很少直接去调用堆相关的api去创建和操作堆,但我们却经常用到堆,比如在c++中的new 操作,new操作就是在堆中分配内存,为了形象显示new创建的堆的过程,我借鉴了如下代码:
class CSomeClass{
public:
static HANDLE s_handle;
static UINT s_allocInheap;
public:
void* operator new(size_t hsize);
void operator delete(void* p);
};
void* CSomeClass::operator new(size_t hsize)
{
if(s_handle==NULL)
{
s_handle=HeapCreate(HEAP_NO_SERIALIZE,0,0);
if(s_handle==NULL)
return NULL;
}
void* p=HeapAlloc(s_handle,0,hsize);
if(p!=NULL)
s_allocInheap++;
else
return NULL;
return (p);
}
上面的代码,覆盖了系统的new操作,当调用一次new操作时,程序就会分配一个内存块并让块数加1.
遍历所有堆
Windows api里有GetProcessHeaps这个函数,该函数可以获取进程创建的所有堆,遍历所有堆之前需要自定义一个句柄数组。GetProcessHeaps函数如下:
HANDLE GetProcessHeaps(
DWORD dWNumHeaps,
PHANDLE phHeaps
);