堆
在系统内部,堆就是一块预定的地址空间区域。 刚开始,区域内的大部分页面都没有调拨物理存储器。随着我们不断地从堆中分配内存,堆管理器会给堆调拨越来越多的物理存储器。这些物理存储器始终是从页交换文件中分配的。释放堆中的内存块时,堆管理器会撤销已调拨的物理存储器。
进程的默认堆
进程初始化的时候,系统会在进程的地址空间中创建一个堆。这个堆被称为进程的默认堆。
在默认情况下,进程默认堆的地址空间区域的大小是1M。
-
改变其大小可以用/HEAP链接器。(仅适用于exe。dll没有与之关联的堆,因此在创建DLL的时不应该适用该链接器)
-
关于/HEAP链接器详见:https://docs.microsoft.com/zh-cn/previous-versions/f90ybzkh(v=vs.120)
-
对默认堆的访问必须依次进行。(系统保证不管在什么时候,一次只让一个线程从默认堆中申请或释放内存块)
-
一个进程同时可以有多个堆,进程在整个生命周期内可以创建和销毁这些堆。
-
默认堆是在程序运行之前由系统自动创建的,在程序终止后会自动销毁。我们无法销毁进程的默认堆。
-
每个堆都有一个用来标识自己的堆句柄,所有分配和释放内存块的对函数都会在参数中用到这个堆句柄。
GetProcessHeap
可以通过GetProcessHeap来得到进程的默认堆句柄。
HANDLE GetProcessHeap();
创建其他的堆
为什么要在进程的地址空间中创建其他的堆(除默认堆)?
- 对组件进行保护 --> 两个独立的堆互不影响
- 更有效的内存管理 --> 如果每个堆只包含同样大小的对象,那么释放一个对象可以保证释放出的空间可以容纳另一个对象
- 局部访问 --> 把内存访问局限在一个较小的地址区间内,将会降低系统需要在内存和磁盘之间进行交换的可能性。
- 避免线程同步的开销 --> 同一时刻有多个线程要访问堆,堆函数必须执行额外的代码来保证堆的线程安全性。如果从堆中进行大量的分配操作,那么额外代码执行过多,会对程序的性能产生影响。创建一个新的堆,使只有一个线程可以访问对,就不必执行额外的代码了,但与此同时,我们自己就要保证堆的线程安全
- 快速释放 --> 把一些数据结构存在一个专门的堆中,我们可以直接释放整个堆,而不用显式地释放每个内存块。
HeapCreate创建自己的进程中额外的堆
HANDLE HeapCreate(
DWORD flOptions,
SIZE_T dwInitialSize,
SIZE_T dwMaximumSize
);
HeapAlloc 从堆中分配内存块
LPVOID HeapAlloc(
HANDLE hHeap,
DWORD dwFlags,
DWORD dwBytes
);
HeapReAlloc 调整内存块的大小
LPVOID HeapReAlloc(
HANDLE hHeap,
DWORD dwFlags,
LPVOID lpMem,
DWORD dwBytes
);
HeapSize 获得内存块的大小
SIZE_T HeapSize(
HANDLE hHeap,
DWORD dwFlags,
LPCVOID lpMem
);
HeapDestroy 销毁堆
BOOL HeapDestroy(
HANDLE hHeap
);