我正在尝试使用protobuf.net序列化相当大量的数据。我遇到了OutOfMemoryException
被抛出的问题。我正在尝试使用IEnumerable<DTO>
来传输数据,以免使用太多内存。这是应该导致错误的程序的简化版本:
class Program
{
static void Main(string[] args)
{
using (var f = File.Create("Data.protobuf"))
{
ProtoBuf.Serializer.Serialize<IEnumerable<DTO>>(f, GenerateData(1000000));
}
using (var f = File.OpenRead("Data.protobuf"))
{
var dtos = ProtoBuf.Serializer.DeserializeItems<DTO>(f, ProtoBuf.PrefixStyle.Base128, 1);
Console.WriteLine(dtos.Count());
}
Console.Read();
}
static IEnumerable<DTO> GenerateData(int count)
{
for (int i = 0; i < count; i++)
{
// reduce to 1100 to use much less memory
var dto = new DTO { Data = new byte[1101] };
for (int j = 0; j < dto.Data.Length; j++)
{
// fill with data
dto.Data[j] = (byte)(i + j);
}
yield return dto;
}
}
}
[ProtoBuf.ProtoContract]
class DTO
{
[ProtoBuf.ProtoMember(1, DataFormat=ProtoBuf.DataFormat.Group)]
public byte[] Data
{
get;
set;
}
}
有趣的是,如果将每个DTO
上的数组大小减小到1100,问题就会消失!在我的实际代码中,我想做类似的事情,但它是一个浮点数组,我将序列化,而不是字节。注:我认为你可以跳过填充数据部分来加速问题。
这是使用protobuf版本2.0.0.594。任何帮助将不胜感激!
编辑:
与版本2.0.0.480相同的问题。代码不能与版本1.0.0.280一起运行。
答案 0 :(得分:3)
K表;这是一个不幸的时间 - 基本上,它只是检查它是否应该在缓冲区满了时刷新,并且由于处于编写长度前缀项目的中间,它始终无法正确刷新那时。我添加了一个调整,以便每当它发现它达到可刷新状态时,和就会有一些值得刷新的东西(目前是1024字节),然后它会更积极地刷新。这已经作为r597承诺。使用该补丁,它现在可以正常工作。
在此期间,有一种方法可以在不改变版本的情况下避免这种故障:在源头迭代数据,使用SerializeWithLengthPrefix
指定前缀式base-128序列化每个单独,和字段编号1;这在线路上是完全相同的,但每个线路都有一个单独的序列化周期:
using (var f = File.Create("Data.protobuf"))
{
foreach(var obj in GenerateData(1000000))
{
Serializer.SerializeWithLengthPrefix<DTO>(
f, obj, PrefixStyle.Base128, Serializer.ListItemTag);
}
}
感谢您的注意; p
答案 1 :(得分:2)
您似乎已超过1.5 GB的限制:Allocating more than 1,000 MB of memory in 32-bit .NET process
您已经注意到,当您减小样本大小时,您的应用程序运行正常。这不是protobuf(我推测)的问题,但是你试图创建一个需要分配超过1.5 GB内存的数组。
这是一个简单的测试:
byte[] data = new byte[2147483648];
这应该会导致OutOfMemoryException
,所以会这样:
byte[][] buffer = new byte[1024][];
for (int i = 0; i < 1024; i++)
{
buffer[i] = new byte[2097152];
}
将数据字节聚合到超过1.5 GB的连续容器中。