我试图更好地理解为什么 Windows Vista 堆的行为方式如此。考虑以下非常简单的程序:
#include <vector>
#define NUM_ALLOCS 10000000
int _tmain(int argc, _TCHAR* argv[])
{
for (int iteration=0; iteration<10000; ++iteration) {
std::vector<unsigned char *> buffer;
buffer.reserve(NUM_ALLOCS);
for (int i=0;i<NUM_ALLOCS;++i) {
buffer.push_back(new unsigned char);
}
for (int i=0;i<NUM_ALLOCS;++i) {
delete buffer[i];
}
}
return 0;
}
基本上,这是一个循环,每次迭代都会分配许多 1 字节块,然后释放它们。当然,该程序的内存使用量在分配缓冲区时会上升,然后在释放缓冲区时会下降。
我在 Windows Vista 64 位上看到的行为是峰值内存使用情况(由任务管理器或 vmmap) 随着时间的推移大致保持不变,而报告的最低内存使用量会不断增长,直到接近内存使用峰值。
在 Windows 7 64 位上,报告的最低内存使用量不会随着时间的推移而增加。
编辑:我已经在两台具有 8 GB / 4 GB RAM 的 Windows Vista 64 位计算机和一台具有 4 GB RAM 的 Windows 7 64 位计算机上进行了测试。我已经在低内存使用情况和高内存使用情况下测试了 8 GB 机器。
编辑:我使用 Visual Studio 2005 和 2010 构建了上面的示例,结果相同。
这个例子没有做任何有用的事情,但是内存使用场景与我的一个程序类似(尽管非常精简),我试图弄清楚为什么它看起来使用的内存比实际使用的内存多得多。据我所知,内存由堆管理器持有。
有人对堆机制有任何见解吗?
我是否需要做一些额外的事情来说服堆管理器完全释放已用的堆内存?我是否应该使用其他策略,例如创建一个单独的堆然后销毁它?
任何意见或见解表示赞赏!
I'm trying to better understand why the Windows Vista heap behaves the way it does. Consider the following very simple program:
#include <vector>
#define NUM_ALLOCS 10000000
int _tmain(int argc, _TCHAR* argv[])
{
for (int iteration=0; iteration<10000; ++iteration) {
std::vector<unsigned char *> buffer;
buffer.reserve(NUM_ALLOCS);
for (int i=0;i<NUM_ALLOCS;++i) {
buffer.push_back(new unsigned char);
}
for (int i=0;i<NUM_ALLOCS;++i) {
delete buffer[i];
}
}
return 0;
}
Basically this is a loop that for each iteration allocates a lot of 1 byte blocks and then releases them. Naturally, the memory usage of this program goes up while allocating the buffers and then down when the buffers are released.
The behavior that I am seeing on Windows Vista 64-bit is that the peak memory usage (as reported by task manager or by vmmap) stays roughly constant over time, whereas the lowest memory usage reported grows until it is close to the peak memory usage.
On Windows 7 64-bit the lowest memory usage reported does not grow over time.
Edit: I've tested on two Windows Vista 64-bit machines with 8 GB / 4 GB RAM and one Windows 7 64-bit machine with 4 GB RAM. I've tested the 8 GB machine with both low and high memory usage scenarios.
Edit: I've built the above example with Visual Studio 2005 and 2010 with the same result.
This example isn't doing anything useful, but the memory usage scenario is similar (albeit heavily condensed) to a program of mine for which I've tried to figure out why it appears to use a lot more memory than it actually does. From what I can tell, the memory is being held by the heap manager.
Does anyone have any insights on the heap mechanisms?
Do I need to do something extra to convince the heap manager to fully release the used heap memory? Are there alternative strategies that I should use, such as creating a separate heap and then destroy it?
Any comments or insights are appreciated!
发布评论
评论(4)
它可能是低碎片堆吗?
在我看来,我在某处读到 Windows 7 默认启用 LFH。但是,快速搜索没有显示确认信息,所以我在这里可能是错的。
不过,有一个简单的方法可以检查。在从 HeapQueryInformation /msdn.microsoft.com/en-us/library/aa366569.aspx" rel="nofollow">GetProcessHeap 并比较不同系统上的结果。
Could it be the Low-Fragmentation Heap?
It seems to me that I read somewhere that LFH is enabled by default on Windows 7. However, quick search didn't reveal the confirmation, so I may be wrong here.
There is an easy way to check, though. Call HeapQueryInformation on a handle obtained from GetProcessHeap and compare the results on different systems.
你在内存压力下尝试过这个吗?除非有其他东西需要,否则释放内存是没有意义的。
Have you tried this under memory pressure? It doesn't make sense to release the memory unless something else needs it.
atzz 是在正确的轨道上,但任何堆都会发生这种行为 - 当您以一字节大小调用第一个“new”时,堆将分配一个“桶”并预分配某个内存块(可能是一些内存块)页面大小的倍数,4K);这样,当后续分配相同大小的内存时,它可以很快地为您提供内存。
此外,当您调用删除时,它只是将该区域标记为未分配,但保留它,以防以后需要类似大小的新对象。
如果堆管理器按照您所描述的方式运行,它会运行得非常慢,因为它必须不断地询问内核,“你能给我另一个字节吗?”和“请解除映射!” (事实上,这是不可能的,因为我记得你可以要求内核给你的最小分配是页面大小)
atzz's on the right track, but this behavior will happen with any heap - when you call that first "new" with the one-byte size, the Heap is going to allocate a "bucket" and preallocate a certain block of memory (probably some multiple of the page size, 4K); this way, when subsequent allocations of the same size come in, it can very quickly give you memory.
Furthermore, when you call delete, it is just marking that region as unallocated, but keeping it around in case you want a new object of similar size later.
If the Heap Manager operated as you describe, it would run extremely slowly because it would have to constantly ask the kernel, "Can you give me another byte?" and "Demap this please!" (in fact, this is impossible since the smallest allocation you can ask the kernel to give you is page sized as I recall)
释放到堆的小内存分配通常放置在用于快速分配的列表中。
即使没有这种优化,堆管理器也可以自由地保留进行分配的堆存储桶。为了将内存返回到系统(VirtualFree'ed),堆管理器必须释放并组合 64KB 块中的所有块。
Small memory allocations that are free'ed to the heap are usually placed into a list that is used for fast allocations.
Even without this optimization, the heap mamager is free to hold into the heap bucket from which the allocation was made. In order for memory to be returned to the system (VirtualFree'ed) all blocks in a 64KB block must be free'ed and combined by the heap manager.