4

我正在尝试使用 protobuf.net 序列化相当大量的数据。我遇到了OutOfMemoryException抛出 s 的问题。我正在尝试使用流式传输数据,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 上运行。

4

2 回答 2

3

ķ; 这是一个不幸的时机 - 基本上,它只是检查是否应该在缓冲区满时刷新,并且由于正在编写一个以长度为前缀的项目,所以它永远无法在那个时候正确刷新. 我添加了一个调整,以便每当它发现它达到可刷新状态,并且有一些值得刷新的东西(当前为 1024 字节)时,它会更积极地刷新。这已作为 r597 提交。有了那个补丁,它现在可以按预期工作了。

在此期间,有一种方法可以在不更改版本的情况下避免这种故障:迭代源数据,使用指定前缀样式 base-128 和字段编号 1单独序列化每个数据;SerializeWithLengthPrefix就通过线路传输的内容而言,这是 100% 相同的,但每个都有一个单独的序列化周期:

using (var f = File.Create("Data.protobuf"))
{
    foreach(var obj in GenerateData(1000000))
    {
        Serializer.SerializeWithLengthPrefix<DTO>(
            f, obj, PrefixStyle.Base128, Serializer.ListItemTag);
    }
}

感谢您的关注;p

于 2012-10-31T20:12:45.850 回答
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 的连续容器中。

于 2012-10-31T16:46:32.460 回答