我的应用程序对大对象进行了大量的二进制序列化和压缩。未压缩的序列化数据集约为 14 MB。压缩后约为 1.5 MB。我发现每当我在我的数据集上调用 serialize 方法时,我的大型对象堆性能计数器就会从 1 MB 以下跃升至大约 90 MB。我也知道,在一个负载相对较重的系统下,通常在运行一段时间(几天)后,这个序列化过程发生了几次,当调用这个序列化方法时,应用程序会抛出内存异常,即使有似乎是足够的内存。我猜碎片化是问题所在(虽然我不能说我 100% 确定,但我已经很接近了)
我能想到的最简单的短期修复(我想我正在寻找短期和长期答案)是在我完成序列化过程后立即调用 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;
尝试使用 UnmanagedMemoryStream 进行二进制序列化后更新
即使我序列化为 UnmanagedMemoryStream,LOH 也会跳到相同的大小。看来无论我做什么,调用 BinaryFormatter 来序列化这个大对象都会使用 LOH。至于预分配,它似乎没有多大帮助。说我预分配说我预分配 100MB,然后我序列化,它将使用 170MB。这是代码。比上面的代码还要简单
BinaryFormatter serializer = new BinaryFormatter();
MemoryStream memoryStream = new MemoryStream(1024*1024*100);
GC.Collect();
serializer.Serialize(memoryStream, assetDS);
中间的 GC.Collect() 只是用来更新 LOH 性能计数器。您将看到它将分配正确的 100 MB。但是当您调用序列化时,您会注意到它似乎将它添加到您已经分配的 100 个之上。