2009-10-19 68 views
8

我正在开发一个可以连续运行数月的VC++ NT服务。它密集使用VC++运行时堆。很明显,堆碎片可能会在某些时候导致故障(认为它已经没有记忆)。如何在我的C++程序中检测和估计堆碎片?

我可以在我的服务上运行哪些测试来估计容易出现堆碎片的程度?

回答

2

我想最好的方法是编写自己的内存管理器(或购买一个)提供这些数据。任何其他方式都会改变堆本身,从而导致结果无效。

更容易实现的策略是分配不同大小的内存块并等待失败 - 但我认为这不是一个好方法。无论如何 - 块的规模越大,没有失败,碎片越少。但取决于内存管理器,分配块可以改变结果。


编辑:我找到了一个关于显示统计信息的slab分配器(注释thx)的链接。尽管如此,这篇文章的英文版本并没有包含那么多的信息。使用babelfish进行翻译。

http://de.wikipedia.org/wiki/Slab_allocatorbabelfish version

http://www.usenix.org/event/usenix01/full_papers/bonwick/bonwick.pdf

+0

+1板分配器。 – user7116 2009-10-19 15:23:15

+0

但是,除非你打算在生产中使用内存管理器,否则更换内存管理器只是为了衡量它。我认为一个更好的解决方案是将实际的内存分配器用于仪器并测量其碎片。你可以通过封装alloc/free调用来做到这一点,或者你可以看看内存管理器是否有可以使用的钩子。 – 2011-09-10 13:32:17

0

我同意托比亚斯 - 使自己的内存管理器是做到这一点的好办法。我知道只有少数开发人员会信任我写这种代码...

另一种可能性是在你的对象上做你自己的垃圾收集/合并 - 在低负载下......即您的服务可以暂时停用一段时间,但它会对其使用的内存进行“碎片整理”,但我不确定如果没有自己的内存管理,您可以保证自己的行为。

5

你已经得到了几个关于如何防止堆碎片问题的答案,但都没有真正直接解决你的问题。估计出现碎片问题的可能性的唯一方法就是模拟大量使用情况,并测量您获得的碎片。

由于这是一项NT服务,所以模拟几个月的使用主要包括大量请求匆忙。有可能你的请求速度比通常预期的要快,所以你可能在几个小时内模拟几个月的请求,甚至可能更少(取决于你通常希望接收请求的速率)。

一旦你模拟了几个月的工作(或者甚至是你这样做),然后你需要看看堆,看看你得到多少碎片。这并不容易,但通常是可以的。你会开始在服务过程中注入一个线程(谷歌搜索“线程注入”或该订单上的东西应该得到相当数量的信息)。然后,你需要走一趟堆,特别是寻找空闲块,但太小而不能满足大多数请求的块。假设你使用的是MS VC++,你可以使用_heapwalk遍历堆,然后它会遍历堆,告诉你堆中每个块的地址,大小和状态(free或in-use)。

最后一个细节:为了产生有意义的结果,可执行文件和包含注入线程的DLL必须链接到DLL中的运行库。这意味着整个过程将会有一堆,所以你的注入线程将走过你的服务所使用的堆。如果您静态链接标准库,则DLL和服务将分别拥有自己的堆。该DLL将走自己的堆,这将告诉你任何有关服务进程正在使用的堆。

0

我确定有一些窗口工具可以给你一个内存状态,但是你应该考虑这个问题来开发你的服务。

首先,您应该了解您执行的分配是什么。我认为最简单的方法是覆盖新的和删除操作符,并从这些新的操作符中计算分配的统计数据,然后调用编译器的默认新增和删除操作符。

我认为你应该计算的最小统计量是公用块大小范围的分配数。

例如0字节到15字节之间 块,16个字节之间的块为32个字节,32个字节之间的块为48个字节,...

还可以添加的每个块的大小范围的顺序分配数

收集完这些数据后,您可以减少碎片问题将您的块对齐到常见尺寸

用于对准最好的和简单的技术是使用一个块认为是2

例如功率对准一个数字,以最接近的数字,16分频,则可以使用下面的函数:

int align(int size) 
{ 
    return ((size + 15) & ~0x0000000F); 
} 

关闭当然,你应该使用你的统计数据来选择2的最佳功率来配合。 目标是达到一个数字,大部分分配将进入少量块范围,并在同一时间保持对齐的开销合理。

好运...

1

Switchin上的低碎片堆的Windows可以帮助在旧系统上做的工作。 对新系统的开机默认(Vista中,服务器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公司(现为微软),这也是在内存碎片很好地概括了一个工具VMMap

1

检测碎片最简单的方法是确定您的程序将执行的最大分配,然后每隔至少分配至少两次。如果分配失败即返回NULL,并通过代码来确定你的堆的使用 - 这样的事情在Windows

PROCESS_MEMORY_COUNTERS counters; 
if(GetProcessMemoryInfo(process, &counters, sizeof(counters))){ 
    result = counters.WorkingSetSize; 
} 

小于系统内存的一定比例通常为75%,那么你肯定拥有一个不成问题。