提示1:正常情况下,程序员是可以不用关心操作系统是怎么维护堆的;但是当堆被破坏,想要查找原因时,排查手段中gflags.exe启用相关堆调试支持的功能时,如果不理解堆的内部结构,估计会一脸懵逼…
提示2:查看堆的内部结构,最好会使用WinDbg,如果不会也没关系,下面我已经添加了查询结果的详细解释
说明:下面都是先给出结构体说明,后给出截图的方式;如果想深入研究页堆等更复杂的结构,一定要对堆的3种基础结构很了解才行
1.整体介绍
堆管理器通过Windows虚拟内存管理器来得到一块内存,这块内存被成为堆段(heap segment)
堆段的基本布局示意图

下面是一个堆段的使用顺序:
- 1.堆段最初被创建时
其中大部分虚拟内存是被保留状态(reserve),只有低地址的一小部分是被提交的(commit)状态
- 2.程序员申请内存
当堆管理器使用完已经提交的内存或不足以提供程序员所需内存,堆段将进一步提交(commit)一部分内存,然后对新的内存进行分割处理,将合适堆块给程序员使用
- 3.堆段使用完所有空间
堆管理器将创建一个新的堆段(heap segment),默认创建新堆段大小是之前的2倍(详细要看算法);会用一个链表统一记录相关的堆段(heap segment)
问题:堆管理器是否会释放某个堆段的内存?
不会,内存会始终保留,但是堆管理器可以取消内存提交(
decommit)
简要介绍一下每个堆都要有的3种数据结构,先看一下结构的层次关系
0:000> dt -r2 ntdll!_HEAP 00a30000 #00a30000为HeapCreate创建堆的句柄(指向堆管理结构的指针)
+0x000 Segment : _HEAP_SEGMENT #描述段的信息,0号段的首地址
+0x000 Entry : _HEAP_ENTRY #描述堆块的信息
#可以看到_HEAP包含_HEAP_SEGMENT和_HEAP_ENTRY,且3个结构的第一个描述的首地址相同
下面是WinDbg的截图

基本结论:每个堆至少拥有一个00号段(Segment),最多可拥有64个段;堆管理器在创建堆时会建立一个段,如果堆是可以增长的话,用完一个段后堆会自动增长;00号段开始处存放着一个HEAP结构的头部信息,每个段都有一个HEAP_SEGMENT结构来描述自己(在每个段的起始处)
2.HEAP结构
堆管理器使用
HEAP结构记录和维护每个堆的管理信息,位于每个堆的开始处
HeapCreate返回的句柄是创建堆的起始地址,即也是指向HEAP结构的指针
#1.ntdll!_HEAP大小
0:000> ?? sizeof(ntdll!_HEAP)
unsigned int 0x258(on600)
#2.列出当前进程的所有堆
0:000> !heap -h
Index Address Name Debugging options enabled
1: 00a90000 #句柄
Segment at 00a90000 to 00b8f000 (00007

最低0.47元/天 解锁文章
6019

被折叠的 条评论
为什么被折叠?



