Mar*_*ark 8 c# memory-management out-of-memory fragmentation large-object-heap
我的应用程序对大对象进行了大量的二进制序列化和压缩.未压缩的序列化数据集大约为14 MB.压缩它是1.5 MB左右.我发现每当我在我的数据集上调用serialize方法时,我的大对象堆性能计数器就会从1 MB以下跳到大约90 MB.我也知道在一个相对繁重的系统下,通常在运行(天)一段时间后,这个序列化过程发生了一段时间,已知应用程序在调用此序列化方法时会丢失内存消除,即使在那里似乎是充足的记忆.我猜测碎片是个问题(虽然我不能说我百分百肯定,我很接近)
最简单的短期修复(我想我正在寻找短期和长期答案)我能想到的是在完成序列化过程后立即调用GC.Collect.在我看来,这将垃圾收集来自LOH的对象,并且可能在其他对象被添加到它之前这样做.这将允许其他对象紧密地紧贴堆中的其余对象,而不会造成太多碎片.
除了这个荒谬的90MB分配,我认为我没有任何其他使用丢失的LOH.这种90 MB的分配也相对较少(每4小时一次).我们当然仍然会有1.5 MB的阵列,也许还有其他一些较小的序列化对象.
有任何想法吗?
由于良好的反应而更新
这是我的代码,它完成了这项工作.我实际上已经尝试将其更改为压缩WHILE序列化,以便序列化同时序列化为流并且我没有得到更好的结果.我还尝试将内存流预先分配到100 MB并尝试连续两次使用相同的流,不过LOH最高可达180 MB.我正在使用Process Explorer来监控它.这太疯狂了.我想我接下来会尝试UnmanagedMemoryStream的想法.
如果你不习惯,我会鼓励你们尝试一下.它不一定是这个确切的代码.只是序列化一个大型数据集,你会得到令人惊讶的结果(我有很多表,15周围和许多字符串和列)
byte[] bytes;
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
System.IO.MemoryStream memStream = new System.IO.MemoryStream();
serializer.Serialize(memStream, obj);
bytes = CompressionHelper.CompressBytes(memStream.ToArray());
memStream.Dispose();
return bytes;
Run Code Online (Sandbox Code Playgroud)
使用UnmanagedMemoryStream尝试二进制序列化后更新
即使我序列化为UnmanagedMemoryStream,LOH也会跳到相同的大小.似乎无论我做什么,调用BinaryFormatter来序列化这个大对象都会使用LOH.至于预分配,它似乎没什么帮助.假设我预分配说我预分配100MB,然后我序列化,它将使用170 MB.这是代码.甚至比上面的代码更简单
BinaryFormatter serializer = new BinaryFormatter();
MemoryStream memoryStream = new MemoryStream(1024*1024*100);
GC.Collect();
serializer.Serialize(memoryStream, assetDS);
Run Code Online (Sandbox Code Playgroud)
中间的GC.Collect()只是为了更新LOH性能计数器.您将看到它将分配正确的100 MB.但是当你调用序列化时,你会注意到它似乎在你已经分配的100之上添加了它.
不幸的是,解决这个问题的唯一方法是将数据分成块,以免在 LOH 上分配大块。这里提出的所有答案都很好,并且预计会起作用,但事实并非如此。.NET 中的二进制序列化(使用 .NET 2.0 SP2)似乎在幕后发挥了自己的小魔力,阻止用户控制内存分配。
那么这个问题的答案是“这不太可能起作用”。当谈到使用 .NET 序列化时,最好的选择是将大对象序列化为较小的块。对于所有其他场景,上面提到的答案都很好。