Finally,还是找到了这个让人"pleasure and pain"的问题的答案!
当在程序中使用HeapCreate()函数来创建私有堆的时候,其中有个诡异的参数——dwMaximumSize,当它指定为0时(我们很高兴),它为可增长堆(growable heap),当它指定为正整数时(-_-),它为不可增长堆(non-growable heap)。
查看MSDN中关于该函数的解释,当dwMaximumSize为非零时,好了,有一句让人费解的话出现了:However, the heap cannot grow, so the maximum size of a memory block in the heap is a bit less than 0x7FFF8 bytes. Requests to allocate larger blocks fail, even if the maximum size of the heap is large enough to contain the block.(然而,这个堆是不可增长的,所以在此堆中一个内存块的最大尺寸应该比0x7FFF8小一些,若在此堆中分配比这更大的内存块,即使堆的最大尺寸足够容纳这个内存块,也不会成功!)
在不可增长堆的一次性分配的内存块尺寸不能超过0x7FFF8,为什么? Baidu,Google,...,nothing!
答案就在堆的实现上,堆管理器(Heap Manager)中的后端管理器。
后端管理器中有个自由列表(free lists),如下图所示:
其中,index1--无用,index2--16B(8B用户请求内存+8B元数据),index3--24B,...,index127--1016B(1008B+8B),从index2到index127所对应的链表用来存放固定大小的自由堆块。
index0用来存放可变大小的自由堆块,其大小范围从1024B(1016B用户请求数据+8B元数据)到0x7FFF8(0x7FFF0用户请求数据+8B),并且从小到大排列(效率较高)。
整个堆的内存是从虚拟内存管理器中分配的(VirtualAlloc()之类),可增长堆与不可增长堆的区别在于:不可增长堆初始化的时候,一旦被预订了指定的内存空间(由HeapCreate的dwMaximumSize参数指定)后,那么无论怎样,它将不会再次请求虚拟内存管理器为之分配内存。而可增长堆是可以的。
当程序需要在堆中分配>=0x7FFF8时,由于在index0所对应的列表中也找不到如此大的内存块,那么堆管理器将尝试请求虚拟内存管理器为之分配大块内存。但不可增长堆是无法再次请求虚拟内存管理器的,所以即使堆中的自由堆块总和能容纳所请求的内存块,也无法为之分配
合适的自由堆块。
0x7FFF8就是一个设计堆时的限制,仅此而已……