ProtoBuf-Net 序列化中的 OutOfMemoryException
OutOfMemoryException in ProtoBuf-Net Serialization
我通过protobuf-net将一些大Dictionary
序列化到磁盘,磁盘文件大小为450MB。
通常,代码因 OutOfMemoryException 而失败
System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
at ProtoBuf.BufferPool.ResizeAndFlushLeft(Byte[]& buffer, Int32 toFitAtLeastBytes, Int32 copyFromIndex, Int32 copyBytes) in c:\Dev\protobuf-net\protobuf-net\BufferPool.cs:line 60
at ProtoBuf.ProtoWriter.StartSubItem(Object instance, ProtoWriter writer, Boolean allowFixed) in c:\Dev\protobuf-net\protobuf-net\ProtoWriter.cs:line 341
at ProtoBuf.ProtoWriter.WriteObject(Object value, Int32 key, ProtoWriter writer) in c:\Dev\protobuf-net\protobuf-net\ProtoWriter.cs:line 44
at proto_15(Object , ProtoWriter )
at ProtoBuf.ProtoWriter.WriteObject(Object value, Int32 key, ProtoWriter writer, PrefixStyle style, Int32 fieldNumber) in c:\Dev\protobuf-net\protobuf-net\ProtoWriter.cs:line 116
at ProtoBuf.Meta.TypeModel.SerializeWithLengthPrefix(Stream dest, Object value, Type type, PrefixStyle style, Int32 fieldNumber, SerializationContext context) in c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:line 548
at ProtoBuf.Meta.TypeModel.SerializeWithLengthPrefix(Stream dest, Object value, Type type, PrefixStyle style, Int32 fieldNumber) in c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:line 515
at ProtoBuf.Serializer.SerializeWithLengthPrefix[T](Stream destination, T instance, PrefixStyle style, Int32 fieldNumber) in c:\Dev\protobuf-net\protobuf-net\Serializer.cs:line 352
机器有 20GB 以上的可用内存。
而我的进程只占用4.6GB
并且 app.config 针对大内存进行了调整
<runtime>
<gcAllowVeryLargeObjects enabled="true" />
<gcServer enabled="true"/>
</runtime>
目前我使用 try-catch
来捕获异常并在几秒后重试。而且它们通常有效。
有没有办法避免这个异常?
gcAllowVeryLargeObjects 对字节缓冲区没有太大帮助,因为元素数量限制仍然适用——但是,令人好奇的是它在 450MB 时突破了;不要误会我的意思——那是相当大的。你在序列化到一个文件吗?网络?或者只是在内存中?如果它不是纯内存中的,可能有一些方法可以说服它需要更少的内部缓冲——例如,切换到组而不是子消息(这只需要一些属性更改)。例如:this
public class Foo {
[ProtoMember(1)]
public List<Bar> Bars {get; set;}
}
将需要在内存中缓冲 Bar
数据,其中-as:
public class Foo {
[ProtoMember(1, DataFormat = DataFormat.Group)]
public List<Bar> Bars {get; set;}
}
不会。
我们在试图通过 WCF 将 300 万条记录从服务端传递到 WPF 客户端的应用程序中遇到了同样的问题。
解决方案是重建服务端,其中序列化为 64 位应用程序(不是 AnyCPU,而是 x64),并确保 APPPool 在 64 位模式下为 运行("Allow 32 bit applications" 设置为 False)。
尽情享受吧!
我通过protobuf-net将一些大Dictionary
序列化到磁盘,磁盘文件大小为450MB。
通常,代码因 OutOfMemoryException 而失败
System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
at ProtoBuf.BufferPool.ResizeAndFlushLeft(Byte[]& buffer, Int32 toFitAtLeastBytes, Int32 copyFromIndex, Int32 copyBytes) in c:\Dev\protobuf-net\protobuf-net\BufferPool.cs:line 60
at ProtoBuf.ProtoWriter.StartSubItem(Object instance, ProtoWriter writer, Boolean allowFixed) in c:\Dev\protobuf-net\protobuf-net\ProtoWriter.cs:line 341
at ProtoBuf.ProtoWriter.WriteObject(Object value, Int32 key, ProtoWriter writer) in c:\Dev\protobuf-net\protobuf-net\ProtoWriter.cs:line 44
at proto_15(Object , ProtoWriter )
at ProtoBuf.ProtoWriter.WriteObject(Object value, Int32 key, ProtoWriter writer, PrefixStyle style, Int32 fieldNumber) in c:\Dev\protobuf-net\protobuf-net\ProtoWriter.cs:line 116
at ProtoBuf.Meta.TypeModel.SerializeWithLengthPrefix(Stream dest, Object value, Type type, PrefixStyle style, Int32 fieldNumber, SerializationContext context) in c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:line 548
at ProtoBuf.Meta.TypeModel.SerializeWithLengthPrefix(Stream dest, Object value, Type type, PrefixStyle style, Int32 fieldNumber) in c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:line 515
at ProtoBuf.Serializer.SerializeWithLengthPrefix[T](Stream destination, T instance, PrefixStyle style, Int32 fieldNumber) in c:\Dev\protobuf-net\protobuf-net\Serializer.cs:line 352
机器有 20GB 以上的可用内存。
而我的进程只占用4.6GB
并且 app.config 针对大内存进行了调整
<runtime>
<gcAllowVeryLargeObjects enabled="true" />
<gcServer enabled="true"/>
</runtime>
目前我使用 try-catch
来捕获异常并在几秒后重试。而且它们通常有效。
有没有办法避免这个异常?
gcAllowVeryLargeObjects 对字节缓冲区没有太大帮助,因为元素数量限制仍然适用——但是,令人好奇的是它在 450MB 时突破了;不要误会我的意思——那是相当大的。你在序列化到一个文件吗?网络?或者只是在内存中?如果它不是纯内存中的,可能有一些方法可以说服它需要更少的内部缓冲——例如,切换到组而不是子消息(这只需要一些属性更改)。例如:this
public class Foo {
[ProtoMember(1)]
public List<Bar> Bars {get; set;}
}
将需要在内存中缓冲 Bar
数据,其中-as:
public class Foo {
[ProtoMember(1, DataFormat = DataFormat.Group)]
public List<Bar> Bars {get; set;}
}
不会。
我们在试图通过 WCF 将 300 万条记录从服务端传递到 WPF 客户端的应用程序中遇到了同样的问题。
解决方案是重建服务端,其中序列化为 64 位应用程序(不是 AnyCPU,而是 x64),并确保 APPPool 在 64 位模式下为 运行("Allow 32 bit applications" 设置为 False)。
尽情享受吧!