【发布时间】:2012-07-04 05:55:37
【问题描述】:
在我们的应用程序中,我们有一些数据结构,其中包含一个分块的字节列表(当前公开为List<byte[]>)。我们将字节分块,因为如果我们允许将字节数组放在大对象堆上,那么随着时间的推移,我们会遭受内存碎片的影响。
我们还开始使用 Protobuf-net 序列化这些结构,使用我们自己生成的序列化 DLL。
但是,我们注意到 Protobuf-net 在序列化时会创建非常大的内存缓冲区。浏览源代码,它似乎在整个List<byte[]> 结构被写入之前无法刷新其内部缓冲区,因为它需要在之后写入缓冲区前面的总长度。
不幸的是,这首先取消了我们对字节进行分块的工作,并最终由于内存碎片而给我们 OutOfMemoryExceptions(异常发生在 Protobuf-net 试图将缓冲区扩展到超过 84k 时,这显然会使它在 LOH 上,我们的整体进程内存使用率相当低)。
如果我对 Protobuf-net 工作原理的分析是正确的,有没有办法解决这个问题?
更新
根据 Marc 的回答,这是我尝试过的:
[ProtoContract]
[ProtoInclude(1, typeof(A), DataFormat = DataFormat.Group)]
public class ABase
{
}
[ProtoContract]
public class A : ABase
{
[ProtoMember(1, DataFormat = DataFormat.Group)]
public B B
{
get;
set;
}
}
[ProtoContract]
public class B
{
[ProtoMember(1, DataFormat = DataFormat.Group)]
public List<byte[]> Data
{
get;
set;
}
}
然后序列化它:
var a = new A();
var b = new B();
a.B = b;
b.Data = new List<byte[]>
{
Enumerable.Range(0, 1999).Select(v => (byte)v).ToArray(),
Enumerable.Range(2000, 3999).Select(v => (byte)v).ToArray(),
};
var stream = new MemoryStream();
Serializer.Serialize(stream, a);
但是,如果我在 ProtoWriter.WriteBytes() 中插入一个断点,它在方法底部调用 DemandSpace() 并进入 DemandSpace(),我可以看到缓冲区没有被刷新,因为 writer.flushLock 等于 @987654329 @。
如果我像这样为 ABase 创建另一个基类:
[ProtoContract]
[ProtoInclude(1, typeof(ABase), DataFormat = DataFormat.Group)]
public class ABaseBase
{
}
[ProtoContract]
[ProtoInclude(1, typeof(A), DataFormat = DataFormat.Group)]
public class ABase : ABaseBase
{
}
那么writer.flushLock 等于2 中的DemandSpace()。
我猜我在这里错过了一个与派生类型有关的明显步骤?
【问题讨论】:
标签: c# protobuf-net chunking large-object-heap