在正文中,我们用的是 std::vector<>
。众所周知,std::vector<>
是 STL 中数组的类似物,适合顺序存储与访问,并不适合随机增删。所以,应该使用 std::list<>
,因为哪个变量要被回收几乎是不可预知的。或者采用改进的方案:更新相对频繁的 Generation 0 和 Generation 1 采用 std::list<>
,相对稳定的 Generation 2 采用 std::vector<>
。
各位应该注意到在 Windows 上运行 100 KB×10000 个单元的测试程序会带来严重的内存“泄露”问题。我以为是 STL 容器或者 new
/delete
操作符的问题,就写了一个纯 C 的版本,但是现象依然如前。后来我发现,如果是 10000 KB×100 个,则会几乎完全回收。在分配内存总量不变的情况下,前者剩余 50 MB 左右,后者几乎为 0。
我们知道,在 CLR 中有一个大对象堆(Large Object Heap),回收策略与小对象堆不一样。
我们也知道,在使用 malloc()
分配内存时(Debug
配置),获得的指针指向的区域会被初始化(用于 CRT 内存检查),反倒是调用 HeapAlloc()
这个 API 的时候才会真正刚刚被操作系统分配内存。
根据现象与部分原理推测,既然 malloc()
和 free()
都是 VC 运行时提供的,内部是不是也有一个不同的策略,让大于某个值的申请空间回收会更彻底呢?
上面的引文中有一句话,说 VCRT 10 下的 free()
操作后状态为:
The memory returned by the
free
is returned to OS, but it stays committed.
关于 Windows 的分页机制我不是很了解,大概只知道是一个 4 KB 对齐、用于常用/不常用内存交换的东西。页的状态对系统对页的操作有何影响,先查阅资料再说吧。
用 2000 个单元进行测试。在我的机器上(Windows 8.1 x64,12 GB)发现在每个单元 500 KB~600 KB 之间回收结果发生了一个突变。我根据 4 KB 对齐猜测阈值可能是 512 KB,但是最终测试的结果是在 507 KB~508 KB 之间。由于这个值可能受当前分配代码导致的页内容的排布影响而变化(例如,32 位下 GC_RECORD
不计编译器附加字段,占用空间 12 B;若是其他的大小,应该会影响空间的页对齐,从而使此值离开上面的区间),所以我认为没法获得一个准确值。
此外我还观察到,10000 KB×100 个和 100 KB×10000 个,实际占用的空间不同,后者比前者多大约 4 MB。这也证实了,编译器会添加附加信息(尽管可能不在代码所用的数据区中)。