使用字节数组时的堆碎片问题

16
我有一个使用C# 4.0编写的单生产者/单消费者应用程序,它以块为单位传输大量数据。尽管没有新的内存分配,但是经过一段时间后我会耗尽内存。
我使用Redgate Memory Profiler对内存进行了分析,发现有很多可用的空闲内存。但由于碎片化,无法使用这些空闲内存。
我使用阻塞集合作为缓冲区,并使用字节数组作为其成员。
BlockingCollection<byte[]> segments = new BlockingCollection<byte[]>(8);
// producer:
segments.Add(buffer);
// consumer:
byte[] buffer = _segments.Take();

我该如何避免托管内存碎片化?

3个回答

10
您可能遇到了大对象堆问题 - 大于85,000字节的对象放在大对象堆中,该堆没有压缩,可能导致奇怪的内存不足情况。尽管显然.NET 4的性能已经得到改善,但仍存在不完美之处。解决方案基本上是使用自己的缓冲池,其中包含一些静态分配的内存块并重复使用它们。
这方面有很多关于SO的问题。
更新:微软作为WCF堆栈的一部分提供缓冲管理器。还有一个在codeproject上的缓冲池

克里斯是正确的,另一个选择是使用小于85k的对象,这样它们就不会被分配到大对象堆上。 - Ian
微软的解决方案对我的情况来说已经足够好了。由于它是抽象的,我应该实现自己的解决方案。那么,我应该为所有数据传输器创建一个静态缓冲区管理器,还是一个单例,或者每个数据传输器一个实例成员(每个数据传输器的生命周期为2-3小时,共有1000个,每个使用8x256 KB内存)? - Xaqron
为每个传输器创建一个缓冲区管理器听起来像是将堆碎片问题移动到另一个地方(分配更大的内存块,然后再释放它)。你可以有一个缓冲区管理器池,但我会先使用单例模式,看看是否有任何问题。你可以设计它,使其隐藏在接口后面,每个传输器都有自己的引用。然后,你可以轻松地在单例和每个实例之间或其他一些方式之间切换实现。 - ChrisWue

4

你的byte[]数组有多长?它们是否属于小对象或大对象堆?如果你遇到内存碎片问题,我会说它们属于LOH。

因此,你应该重用相同的byte数组(使用池)或使用较小的块。LOH从不压缩,因此可能变得非常碎片化。不幸的是,没有什么办法可以解决这个问题。(除了知道这个限制并避免它)


我使用for循环大约2-3小时,8x256 KB,内存分析器显示它是大对象堆。我该如何创建一个字节数组池?(欢迎提供有用的链接) - Xaqron

0

GC 不会自动为您压缩大对象堆,但您仍然可以通过编程方式进行压缩。以下代码片段示例说明了如何实现此目的。

GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();

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