一、为什么要使用堆 (Heap)?
应用程序虽然可以使用页面粒度的函数(如VirualAlloc
)来分配一个最小为4KB
或8K
的内存块,但是很多时候我们并不需要分配这么大的内存块,我们可能只想分配 1K,2K 的内存块,那么这个时候无论从内存的使用率,还是从性能的角度来看,再分配这么大的一个内存区域显然不是最优的了。
为了满足这种需求,Windows 提供了一个被称为“堆管理器”
的组件,它负责管理大内存区域中的内存分配,这些大内存区域就是通过一些页面粒度的内存分配函数(如VirualAlloc
)来预定(reserve
)的。
堆管理器中的分配粒度相对比较小:在32位系统上是8字节,在64位系统上是16字节
。
堆管理器已经被 Windows 系统精心设计,在这些小内存分配的情况下会进行内存使用率和性能两个方面的优化。
二、进程的默认堆
每个进程至少有一个堆,那就是进程的默认堆
。进程的默认堆
是在进程启动的时候创建的,而且在进程的生命周期中永远不会被删除。
“默认堆”
的默认大小为1MB
,但是可以通过/HEAP
链接器编译器选项来指定一个更大的起始大小。这个大小值只是初始的保留内存大小,后期根据需要它可以自动扩充。
应用程序可以调用GetProcessHeap
来获取进程的默认堆,也可以通过调用HeapCreate
函数来创建额外的私有堆,当一个进程不在需要一个私有堆的时候,它可以调用HeapDestory
来释放虚拟地址空间。
三、crt 堆
C 语言的malloc,free
函数以及 C++的new,delete
都是从堆
上分配和释放内存的。但是他们所使用的堆不是进程的默认堆
,他们使用的是私有堆
。可是我们在使用malloc
函数之前并有进行任何私有堆的创建操作呀? 因为malloc
函数使用的这个私有堆不需要程序员来创建,而是在 C 或 C++运行时库 DLL 的启动代码_DllMainCRTStartup
中自动创建的。下面通过解析malloc
函数的调用过程来说明这一点。
malloc
函数的定义在malloc.c
文件中,调用流程如下:
(以Microsoft Visual Studio 10.0
为例,malloc.c
文件路径为C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src
)
1 | (1). void* __cdecl malloc(size_t const size) |
_heap_alloc
的定义如下:
1 | __forceinline void * __cdecl _heap_alloc (size_t size) |
从上面的代码中,我们可以看到分配内存块的时候使用的是_crtheap
句柄标记的堆。那么_crtheap
堆是何时创建的了?
我们从heapinit.c
文件中的_heap_init
函数可以看到_crtheap
堆的创建过程:
1 | HANDLE _crtheap=NULL; |
从上面的代码,我们可以看到,创建的私有堆
句柄存放在一个全局的_crtheap变量
中,后面每次调用malloc
函数都是从该堆分配内存块。
四、Win32 堆函数
我们最常用的 Windows 堆函数如下:
HeapCreate
或HeapDestory
— 创建或删除一个私有堆HeapAlloc
— 分配一个堆内存块HeapFree
— 释放一个原先由HeapAlloc
分配的内存块HeapReAlloc
— 增长或缩减一个已分配的内存块的大小HeapLock
或HeapUnLock
— 控制堆操作的内存访问HeapWalk
— 列举一个堆内部的内存项和区域。
五、Windows 内存管理 API 分层结构
从上图可以看到,虚拟内存机制(Virtual Memory
)是 windows 内存体系的基础,无论你是使用堆
,还是使用内存映射文件
,它们的底层都是基于虚拟内存来实现的。
从上往下,每一层的 API 在内部会依次调用下一层的 API。下图中列举了每层 API 中经常使用的函数:
- CRT Memory Functions:
malloc, free, new, delete
- Local, Global Memory API: **
LocalAlloc, GlobalAlloc
(这 2 个函数现在不建议使用,注意是为了兼容以前的老代码才保留下来的**) - Heap Memory API:
HeapCreate, HeapAlloc, HeapDestory
- Virtual Memory API:
VirtualAlloc, VirtualFree
- Memory Mapped File API:
CreateFileMapping, MapViewOfFile, MapViewOfFileEx, UnMapViewOfFile
文章图片带有“CSDN”水印的说明:
由于该文章和图片最初发表在我的CSDN 博客中,因此图片被 CSDN 自动添加了水印。