我正在开发一个VC ++ NT服务,该服务旨在连续运行数月。它密集使用VC ++运行时堆。很明显,堆碎片在某些时候会导致它出现故障(认为它是内存不足)。
我可以在我的服务上运行哪些测试来估计容易出现堆碎片的程度?
答案 0 :(得分:5)
你已经得到了几个答案,讨论如何防止堆碎片问题,但两者都没有直接解决你的问题。几乎唯一能够估计出现碎片问题的可能性的方法是模拟大量使用,并测量你得到的碎片。
由于这是一项NT服务,因此模拟使用数月主要包括匆忙提出大量请求。有可能您可以比通常预期的更快地发出请求,因此您可以在几个小时内模拟几个月的请求,甚至可能更少(取决于您通常期望接收请求的速率) )。
一旦你模拟了几个月的工作(或者甚至在你这样做的时候),你需要查看堆,看看你得到了多少碎片。这并不容易,但通常是可能的。您将首先在服务进程中注入一个线程(在线程注入上搜索“或者该命令中的某些内容应该获得相当多的信息)。然后你需要走遍堆,寻找(特别是)可用的块,但是太小而不能满足大多数请求。假设您正在使用MS VC ++,那么您将使用_heapwalk遍历堆,并且它将遍历堆,告诉您堆中每个块的地址,大小和状态(空闲或正在使用)。
最后一个细节:为了产生有意义的结果,包含注入线程的可执行文件和DLL都必须链接到DLL中的运行时库。这意味着整个进程将有一个堆,因此您注入的线程将遍历您的服务使用的堆。如果静态链接标准库,则DLL和服务都将拥有自己的堆。 DLL将遍历自己的堆,它不会告诉您服务进程正在使用的堆。
答案 1 :(得分:2)
我想最好的方法是编写自己的内存管理器(或购买一台)来提供这些数据。任何其他方式都会改变堆本身,从而使结果无效。
更容易实现的策略是分配不同大小的内存块并等待失败 - 但我认为这不是一个好方法。无论如何 - 块大小越大,没有失败,碎片越少。但是根据内存管理器的不同,分配块可能会改变结果。
编辑:我找到了一个关于slab分配器(注释的thx)的链接,显示了统计信息。虽然它是德语,但该文章的英文版本并未包含太多信息。使用babelfish进行翻译。
http://de.wikipedia.org/wiki/Slab_allocator (babelfish version)
http://www.usenix.org/event/usenix01/full_papers/bonwick/bonwick.pdf
答案 2 :(得分:1)
在Windows的低碎片堆上切换可以帮助在旧系统上完成工作。 在新系统上它的默认开启(Vista,Server 2008)
HANDLE heaps[1025];
DWORD nheaps = GetProcessHeaps((sizeof(heaps) / sizeof(HANDLE)) - 1, heaps);
for (DWORD i = 0; i < nheaps; ++i) {
ULONG enableLFH = 2;
HeapSetInformation(heaps[i], HeapCompatibilityInformation, &enableLFH, sizeof(enableLFH));
}
sysinternals(现在是Microsoft)提供了一个工具VMMap,它可以很好地概述内存碎片。
答案 3 :(得分:1)
检测碎片的最简单方法是确定您的程序将进行的最大分配,然后每次分配至少两倍的数量。如果分配失败,则返回NULL并且您的堆使用由代码确定 - 在Windows上就像这样
PROCESS_MEMORY_COUNTERS counters;
if(GetProcessMemoryInfo(process, &counters, sizeof(counters))){
result = counters.WorkingSetSize;
}
小于系统内存的某个百分比,通常为75%,那么你肯定会出现碎片问题。
答案 4 :(得分:0)
我同意Tobias - 让自己的内存管理器成为一个很好的方法。我知道只有少数开发人员会信任我写这种代码......
另一种可能性是在你的对象上不时地进行自己的垃圾收集/整合 - 在低负载下...即你的服务可以暂时不活动,同时它“碎片化”它使用的内存但是我我不确定如果没有自己的内存管理,你可以保证你想要的行为。
答案 5 :(得分:0)
我确信有一些Windows工具可以为您提供内存状态,但是您应该考虑到这个问题来开发服务。
首先,你应该了解你预先形成的分配。我认为这样做的简单方法是覆盖new和delete运算符,并且从这些新运算符中,您应该计算一些分配的统计信息,然后调用编译器的默认new和delete运算符。
我认为您应该计算的最小统计数据是公共块大小范围的分配数量。
e.g。 0字节到15字节之间的块,16字节到32字节之间的块,32字节到48字节之间的块,......
您还可以添加每个块大小范围的顺序分配数
收集此数据后,您可以通过将块与常规尺寸对齐来减少碎片问题。
最佳和简单的对齐技术是使用功率为2的块。
例如,要将数字与最接近16的数字对齐,可以使用以下函数:
int align(int size)
{
return ((size + 15) & ~0x0000000F);
}
当然,你应该使用你的统计数据来选择2的最佳幂来对齐。 目标是达到一个数字,大多数分配将进入几个块范围,同时保持对齐的开销合理。
祝你好运......