在我们的应用程序中,我们有一些数据结构,其中包含一组分块的字节(目前暴露为
我们还开始使用Protobuf-net来序列化这些结构,使用我们自己生成的序列化DLL。
然而,我们注意到Protobuf-net在序列化时创建了非常大的内存缓冲区。浏览源代码,似乎它不能刷新其内部缓冲区,直到整个
不幸的是,这样做破坏了我们对字节进行分块的工作,并最终由于内存碎片而导致OutOfMemoryExceptions(异常发生在Protobuf-net试图将缓冲区扩展到超过84k的时候,这显然将其放在LOH上,而我们的整个进程内存使用率相当低)。
如果我对Protobuf-net的工作方式的分析是正确的,那么有没有解决这个问题的方法?
更新
根据Marc的答案,这是我尝试过的:
然后进行序列化:
然而,如果我在
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
等于1
。
如果我创建另一个ABase的基类如下:
[ProtoContract]
[ProtoInclude(1, typeof(ABase), DataFormat = DataFormat.Group)]
public class ABaseBase
{
}
[ProtoContract]
[ProtoInclude(1, typeof(A), DataFormat = DataFormat.Group)]
public class ABase : ABaseBase
{
}
在 DemandSpace()
中,writer.flushLock
的值等于 2
。
我猜这里可能有一个明显的步骤与派生类型有关?