C++ 如何解决内存碎片

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/60871/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 12:34:09  来源:igfitidea点击:

How to solve Memory Fragmentation

c++windowsmemory

提问by Alan

We've occasionally been getting problems whereby our long-running server processes (running on Windows Server 2003) have thrown an exception due to a memory allocation failure. Our suspicion is these allocations are failing due to memory fragmentation.

我们偶尔会遇到问题,我们的长期运行的服务器进程(在 Windows Server 2003 上运行)由于内存分配失败而引发异常。我们怀疑这些分配由于内存碎片而失败。

Therefore, we've been looking at some alternative memory allocation mechanisms that may help us and I'm hoping someone can tell me the best one:

因此,我们一直在寻找一些可能对我们有帮助的替代内存分配机制,我希望有人能告诉我最好的一种:

1) Use Windows Low-fragmentation Heap

1) 使用 Windows低碎片堆

2) jemalloc - as used in Firefox 3

2) jemalloc - 在Firefox 3 中使用

3) Doug Lea's malloc

3) Doug Lea 的malloc

Our server process is developed using cross-platform C++ code, so any solution would be ideally cross-platform also (do *nix operating systems suffer from this type of memory fragmentation?).

我们的服务器进程是使用跨平台 C++ 代码开发的,因此任何解决方案都是理想的跨平台解决方案(*nix 操作系统是否遭受这种类型的内存碎片?)。

Also, am I right in thinking that LFH is now the default memory allocation mechanism for Windows Server 2008 / Vista?... Will my current problems "go away" if our customers simply upgrade their server os?

另外,我认为 LFH 现在是 Windows Server 2008 / Vista 的默认内存分配机制是否正确?...如果我们的客户只是升级他们的服务器操作系统,我当前的问题会“消失”吗?

采纳答案by Tall Jeff

First, I agree with the other posters who suggested a resource leak. You really want to rule that out first.

首先,我同意其他建议资源泄漏的海报。你真的想先排除这一点。

Hopefully, the heap manager you are currently using has a way to dump out the actual total free space available in the heap (across all freeblocks) and also the total number of blocks that it is divided over. If the average free block size is relatively small compared to the total free space in the heap, then you do have a fragmentation problem. Alternatively, if you can dump the size of the largest free block and compare that to the total free space, that will accomplish the same thing. The largest free block would be small relative to the total freespace available across all blocks if you are running into fragmentation.

希望您当前使用的堆管理器有一种方法可以转储堆中可用的实际总可用空间(跨所有空闲块)以及它被划分的块总数。如果平均空闲块大小与堆中的总空闲空间相比相对较小,那么您确实存在碎片问题。或者,如果您可以转储最大空闲块的大小并将其与总空闲空间进行比较,则将完成相同的操作。如果遇到碎片,最大的空闲块相对于所有块中可用的总可用空间较小。

To be very clear about the above, in all cases we are talking about freeblocks in the heap, not the allocated blocks in the heap. In any case, if the above conditions are not met, then you dohave a leak situation of some sort.

要非常清楚上述内容,在所有情况下,我们都在讨论堆中的空闲块,而不是堆中已分配的块。无论如何,如果不满足上述条件,那么您确实存在某种泄漏情况。

So, once you have ruled out a leak, you could consider using a better allocator. Doug Lea's mallocsuggested in the question is a very good allocator for general use applications and very robust mostof the time. Put another way, it has been time tested to work very well for most any application. However, no algorithm is ideal for allapplications and any management algorithm approach can be broken by the right pathelogical conditions against it's design.

因此,一旦排除了泄漏,就可以考虑使用更好的分配器。Doug Lea在问题中建议的 malloc是一个非常好的通用应用程序分配器,并且在大多数情况下非常健壮。换句话说,它已经过时间测试,可以很好地适用于大多数任何应用程序。然而,没有一种算法适合所有应用程序,任何管理算法方法都可能被违反其设计的正确病理条件所破坏。

Why are you having a fragmentation problem?- Sources of fragmentation problems are causedby the behavior of an application and have to do with greatly different allocation lifetimes in the same memory arena. That is, some objects are allocated and freed regularly while other types of objects persist for extended periods of time all in the same heap.....think of the longer lifetime ones as poking holes into larger areas of the arena and thereby preventing the coalesce of adjacent blocks that have been freed.

为什么会出现碎片问题?- 碎片问题的根源是由应用程序的行为引起的,并且与同一内存领域中非常不同的分配生命周期有关。也就是说,一些对象被定期分配和释放,而其他类型的对象在同一个堆中持续很长时间......将生命周期更长的对象视为在竞技场的更大区域中戳洞,从而防止已释放的相邻块的合并。

To address this type of problem, the best thing you can do is logically divide the heap into sub arenas where the lifetimes are more similar. In effect, you want a transient heap and a persistent heap or heaps that group things of similar lifetimes.

为了解决此类问题,您可以做的最好的事情是在逻辑上将堆划分为生命周期更相似的子区域。实际上,您需要一个临时堆和一个或多个持久堆,它们将具有相似生命周期的事物分组在一起。

Some others have suggested another approach to solve the problem which is to attempt to make the allocation sizes more similar or identical, but this is less ideal because it creates a different type of fragmentation called internal fragmentation - which is in effect the wasted space you have by allocating more memory in the block than you need.

其他一些人提出了另一种解决问题的方法,即尝试使分配大小更相似或相同,但这不太理想,因为它创建了一种称为内部碎片的不同类型的碎片 - 这实际上是浪费的空间通过在块中分配比您需要的更多的内存。

Additionally, with a good heap allocator, like Doug Lea's, making the block sizes more similar is unnecessary because the allocator will already be doing a power of two size bucketing scheme that will make it completely unnecessary to artificially adjust the allocation sizes passed to malloc() - in effect, his heap manager does that for you automatically much more robustly than the application will be able to make adjustments.

此外,对于像 Doug Lea 那样的良好堆分配器,不需要使块大小更相似,因为分配器已经在执行两个大小的分桶方案,这将完全没有必要人为调整传递给 malloc( ) - 实际上,他的堆管理器会自动为您执行此操作,这比应用程序能够进行的调整要强大得多。

回答by Tal

I think you've mistakenly ruled out a memory leak too early. Even a tiny memory leak can cause a severe memory fragmentation.

我认为您过早地错误地排除了内存泄漏。即使是很小的内存泄漏也会导致严重的内存碎片。

Assuming your application behaves like the following:
Allocate 10MB
Allocate 1 byte
Free 10MB
(oops, we didn't free the 1 byte, but who cares about 1 tiny byte)

假设您的应用程序的行为如下:
分配 10MB
分配 1 个字节
空闲 10MB
(哎呀,我们没有释放 1 个字节,但谁在乎 1 个小字节)

This seems like a very small leak, you will hardly notice it when monitoring just the total allocated memory size. But this leak eventually will cause your application memory to look like this:
.
.
Free – 10MB
.
.
[Allocated -1 byte]
.
.
Free – 10MB
.
.
[Allocated -1 byte]
.
.
Free – 10MB
.
.

这似乎是一个非常小的泄漏,仅在监视总分配的内存大小时您几乎不会注意到它。但这种泄漏最终会导致应用程序的内存看起来像这样:

.
免费 – 10MB

.
[已分配 -1 字节]

.
免费 – 10MB

.
[已分配 -1 字节]

.
免费 – 10MB

.

This leak will not be noticed... until you want to allocate 11MB
Assuming your minidumps had full memory info included, I recommend using DebugDiagto spot possible leaks. In the generated memory report, examine carefully the allocation count (not size).

这种泄漏不会被注意到...直到您想要分配 11MB
假设您的小型转储包含完整的内存信息,我建议使用DebugDiag来发现可能的泄漏。在生成的内存报告中,仔细检查分配计数(不是大小)

回答by Mark

As you suggest, Doug Lea's malloc might work well. It's cross platform and it has been used in shipping code. At the very least, it should be easy to integrate into your code for testing.

正如您所建议的,Doug Lea 的 malloc 可能会很好地工作。它是跨平台的,并已用于运输代码。至少,它应该很容易集成到您的代码中进行测试。

Having worked in fixed memory environments for a number of years, this situation is certainly a problem, even in non-fixed environments. We have found that the CRT allocators tend to stink pretty bad in terms of performance (speed, efficiency of wasted space, etc). I firmly believe that if you have extensive need of a good memory allocator over a long period of time, you should write your own (or see if something like dlmalloc will work). The trick is getting something written that works with your allocation patterns, and that has more to do with memory management efficiency as almost anything else.

在固定内存环境中工作了多年,这种情况肯定是一个问题,即使在非固定环境中也是如此。我们发现 CRT 分配器在性能(速度、浪费空间的效率等)方面往往很糟糕。我坚信,如果你长期需要一个好的内存分配器,你应该自己编写(或者看看像 dlmalloc 这样的东西是否可行)。诀窍是编写一些适合您的分配模式的内容,这与几乎所有其他事情一样与内存管理效率有关。

Give dlmalloc a try. I definitely give it a thumbs up. It's fairly tunable as well, so you might be able to get more efficiency by changing some of the compile time options.

试试 dlmalloc。我绝对竖起大拇指。它也是相当可调的,因此您可以通过更改一些编译时选项来提高效率。

Honestly, you shouldn't depend on things "going away" with new OS implementations. A service pack, patch, or another new OS N years later might make the problem worse. Again, for applications that demand a robust memory manager, don't use the stock versions that are available with your compiler. Find one that works for yoursituation. Start with dlmalloc and tune it to see if you can get the behavior that works best for your situation.

老实说,您不应该依赖新操作系统实现“消失”的东西。服务包、补丁或其他新操作系统 N 年后可能会使问题变得更糟。同样,对于需要强大内存管理器的应用程序,不要使用编译器可用的库存版本。找到一种适合情况的方法。从 dlmalloc 开始并对其进行调整,以查看是否可以获得最适合您情况的行为。

回答by Fire Lancer

You can help reduce fragmentation by reducing the amount you allocate deallocate.

您可以通过减少分配解除分配的数量来帮助减少碎片。

e.g. say for a web server running a server side script, it may create a string to output the page to. Instead of allocating and deallocating these strings for every page request, just maintain a pool of them, so your only allocating when you need more, but your not deallocating (meaning after a while you get the situation you not allocating anymore either, because you have enough)

例如,对于运行服务器端脚本的 Web 服务器,它可能会创建一个字符串来输出页面。不是为每个页面请求分配和取消分配这些字符串,只需维护一个它们的池,所以您只有在需要更多时才分配,但不会取消分配(意思是一段时间后,您也不会再分配了,因为您有足够的)

You can use _CrtDumpMemoryLeaks(); to dump memory leaks to the debug window when running a debug build, however I believe this is specific to the Visual C compiler. (it's in crtdbg.h)

您可以使用 _CrtDumpMemoryLeaks(); 在运行调试版本时将内存泄漏转储到调试窗口,但是我相信这是特定于 Visual C 编译器的。(它在 crtdbg.h 中)

回答by Alan

@nsaners - I'm pretty sure the problem is down to memory fragmentation. We've analyzed minidumpsthat point to a problem when a large (5-10mb) chunk of memory is being allocated. We've also monitored the process (on-site and in development) to check for memory leaks - none were detected (the memory footprint is generally quite low).

@nsaners - 我很确定问题出在内存碎片上。我们分析了在分配大 (5-10mb) 内存块时指向问题的小型转储。我们还监视了过程(现场和开发中)以检查内存泄漏 - 没有检测到(内存占用通常很低)。

回答by maccullt

The problem does happen on Unix, although it's usually not as bad.

这个问题确实发生在 Unix 上,虽然它通常没有那么糟糕。

The Low-framgmentation heap helped us, but my co-workers swear by Smart Heap(it's been used cross platform in a couple of our products for years). Unfortunately due to other circumstances we couldn't use Smart Heap this time.

Low-fragmentation heap 帮助了我们,但我的同事们对Smart Heap发誓 (多年来,它已在我们的几个产品中跨平台使用)。不幸的是,由于其他情况,我们这次无法使​​用 Smart Heap。

We also look at block/chunking allocating and trying to have scope-savvy pools/strategies, i.e., long term things here, whole request thing there, short term things over there, etc.

我们还研究了块/分块分配并尝试使用范围精明的池/策略,即这里的长期事物,那里的整个请求事物,那里的短期事物等。

回答by dicroce

As usual, you can usually waste memory to gain some speed.

像往常一样,您通常可以浪费内存来获得一些速度。

This technique isn't useful for a general purpose allocator, but it does have it's place.

这种技术对于通用分配器没有用处,但它确实有它的用处。

Basically, the idea is to write an allocator that returns memory from a pool where all the allocations are the same size. This pool can never become fragmented because any block is as good as another. You can reduce memory wastage by creating multiple pools with different size chunks and pick the smallest chunk size pool that's still greater than the requested amount. I've used this idea to create allocators that run in O(1).

基本上,这个想法是编写一个分配器,它从一个池中返回内存,其中所有分配的大小都相同。这个池永远不会碎片化,因为任何块都和另一个一样好。您可以通过创建多个具有不同大小块的池并选择仍然大于请求数量的最小块大小池来减少内存浪费。我已经使用这个想法来创建在 O(1) 中运行的分配器。

回答by nsanders

I'd suspect a leak before suspecting fragmentation.

在怀疑碎片之前,我怀疑有泄漏。

For the memory-intensive data structures, you could switch over to a re-usable storage pool mechanism. You might also be able to allocate more stuff on the stack as opposed to the heap, but in practical terms that won't make a huge difference I think.

对于内存密集型数据结构,您可以切换到可重用的存储池机制。您可能还可以在堆栈上分配更多的东西而不是堆,但实际上我认为这不会产生巨大的差异。

I'd fire up a tool like valgrind or do some intensive logging to look for resources not being released.

我会启动一个像 valgrind 这样的工具,或者做一些密集的日志记录来寻找没有被释放的资源。

回答by Danil

if you talking about Win32 - you can try to squeeze something by using LARGEADDRESSAWARE. You'll have ~1Gb extra defragmented memory so your application will fragment it longer.

如果你在谈论 Win32 - 你可以尝试使用 LARGEADDRESSAWARE 来压缩一些东西。您将拥有约 1Gb 的额外碎片整理内存,因此您的应用程序将对其进行更长时间的碎片整理。

回答by kris2k

The simple, quick and dirty, solution is to split the application into several process, you should get fresh HEAP each time you create the process.

简单、快速和肮脏的解决方案是将应用程序拆分为多个进程,每次创建进程时都应该获得新的 HEAP。

Your memory and speed might suffer a bit (swapping) but fast hardware and big RAM should be able to help.

您的内存和速度可能会受到一点影响(交换),但快速的硬件和大内存应该能够提供帮助。

This was old UNIX trick with daemons, when threads did not existed yet.

当线程还不存在时,这是使用守护进程的老 UNIX 技巧。