如何解决C++服务器程序中的堆碎片问题?

3

堆碎片化可能会导致服务器应用程序在预计运行数月后突然开始出现故障,认为它已经耗尽了内存。

假设我已经尽力减少在我的VC++服务器应用程序中运行时堆碎片化,但仍然会不断地增加并引起问题。例如,我可以每个月或每处理50万个请求时自动重新启动应用程序 - 安全地停止并安全地启动一个新的堆。除此之外,我还能做些什么来解决堆碎片化的问题?

4个回答

3
一个很好的起点是启用低碎片堆,并检查它是否仍然会产生碎片。
  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));
  }

这个新的内存管理器在Vista/Server 2008上默认开启... 所以如果你觉得新的服务器操作系统更好,这可能是原因之一。
低碎片堆是在Windows 2000的服务包中引入的,但直到Windows Vista才能主动启用。
有一个工具vmmap可以轻松查看内存概况,并提供良好的概述,以确定是否发生了碎片化。

VMMap是一个很好的发现。谢谢。 - user172783
1
注意,上面的代码有两个错误!1. nheaps 可能大于 1023。这可能导致访问冲突。请阅读 MSDN 上的 GetProcessHeaps。首先像这样调用它:GetProcessHeaps(0,NULL) 来获取堆计数,然后分配适当的缓冲区。2. MSDN 明确指出,您应仅将 GetProcessHeaps 结果用于调试目的。因此,一旦获取了堆,它们可能已经更改(即关闭)。使用此类句柄是未定义的行为... - denis-bu

3

与此处相同的答案:如何检测和估计C++程序中的堆碎片?

编写适用于您的内存分配模式的自己的内存管理器。或者购买一个(例如smart-heap)。

由于碎片化取决于您的内存分配模式/释放模式,因此很难给出更好的答案。但是,您可以查看固定大小的分配器,或查看smart heap page的分配处理方式。此主题还有许多论文。例如,尝试www.memorymanagement.org

或者您可以查看FastMM4 - 这是开源的,但是使用Pascal / Delphi。

还有一些编程技巧。最值得注意的是:Object Pool。在这种情况下,由于对象被重用而不是释放,因此没有碎片化。但我认为固定大小分配器比对象池表现更好。以这种方式使用对象池只是“穷人版”的固定大小分配器。


这些是最小化碎片的好方法,但不适用于解决它。 - sharptooth
我假设Sharptooth询问在他的程序检测到堆碎片时该怎么办。 - Nick Dandoulakis
是的,没错。一种策略就是定期重新启动。我还能做什么? - sharptooth
防止堆碎片化。否则:尽可能释放内存,然后重新分配。尝试释放靠近一起的内存区域。 - Tobias Langner

0

与其基于时间或请求数量安排重启,你可以检查堆以查看当碎片化达到某个水平时最大连续内存块的大小低于一定水平 - 说到底,当你尝试分配大于堆中最大空闲连续空间大小的对象时,你将开始看到内存不足错误,而不是所有内存都已用完。

你可以使用VirtualQueryEx遍历堆并找到最大的自由连续区域。关于如何做到这一点的示例,请参见this article


0

显而易见的解决方法是挖掘过去发明的旧方案。例如,使用不透明句柄来代替原始指针来处理可移动对象。这样可以使堆进行碎片整理。


这就是苹果的 Mac 如何能够在 128k 的内存中运行整个窗口操作系统和应用程序。如今这似乎是不可能的。 - AnthonyLambert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接