C# 序列化包含大字节数组的对象列表时引发OutOfMemoryException

C# 序列化包含大字节数组的对象列表时引发OutOfMemoryException,c#,protobuf-net,C#,Protobuf Net,我正试图用protobuf.net序列化大量的数据。我遇到了抛出OutOfMemoryExceptions的问题。我正在尝试使用IEnumerable来流式传输数据,以免占用太多内存。以下是导致错误的程序的简化版本: class Program { static void Main(string[] args) { using (var f = File.Create("Data.protobuf")) { ProtoBuf

我正试图用protobuf.net序列化大量的数据。我遇到了抛出
OutOfMemoryException
s的问题。我正在尝试使用
IEnumerable
来流式传输数据,以免占用太多内存。以下是导致错误的程序的简化版本:

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;
    }
}
类程序
{
静态void Main(字符串[]参数)
{
使用(var f=File.Create(“Data.protobuf”))
{
ProtoBuf.Serializer.Serialize(f,GenerateData(1000000));
}
使用(var f=File.OpenRead(“Data.protobuf”))
{
var dtos=ProtoBuf.Serializer.DeserializeItems(f,ProtoBuf.PrefixStyle.Base128,1);
Console.WriteLine(dtos.Count());
}
Console.Read();
}
静态IEnumerable生成数据(整数计数)
{
for(int i=0;i
有趣的是,如果您将每个
DTO
上的数组大小减小到1100,问题就会消失!在我的实际代码中,我想做一些类似的事情,但我要序列化的是一个浮点数组,而不是字节。注意:我想你可以跳过数据填充部分来加速问题的解决

这是使用protobuf版本2.0.0.594。任何帮助都将不胜感激

编辑:


版本2.0.0.480也存在同样的问题。代码无法在版本1.0.0.280下运行。

您似乎超过了1.5 GB的限制:

您已经注意到,当您减小样本大小时,您的应用程序运行良好。这不是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];
}
byte[][]缓冲区=新字节[1024][];
对于(int i=0;i<1024;i++)
{
缓冲区[i]=新字节[2097152];
}

有些东西正在将数据字节聚合到一个超过1.5 GB的连续容器中。

k;这是一个不幸的时机-基本上,它只是检查它是否应该刷新每当缓冲区满了,作为一个结果是在写一个长度前缀项目,它从来没有能够正确地冲洗在这一点上。我添加了一个调整,这样每当它发现它达到可刷新状态,并且有值得刷新的内容(当前为1024字节)时,它就会更积极地刷新。这已作为r597承诺。有了这个补丁,它现在可以正常工作了

在此期间,有一种在不更改版本的情况下避免此故障的方法:在源代码处迭代数据,使用
SerializeWithLengthPrefix
分别序列化每个,指定前缀样式base-128和字段编号1;这是100%完全相同的,但有一个单独的序列化周期:

using (var f = File.Create("Data.protobuf"))
{
    foreach(var obj in GenerateData(1000000))
    {
        Serializer.SerializeWithLengthPrefix<DTO>(
            f, obj, PrefixStyle.Base128, Serializer.ListItemTag);
    }
}
使用(var f=File.Create(“Data.protobuf”))
{
foreach(生成数据中的var obj(1000000))
{
Serializer.SerializeWithLength前缀(
f、 obj,PrefixStyle.Base128,Serializer.ListItemTag);
}
}

感谢您的关注;p

文件保存到磁盘时有多大(MB,GB)?@Lirik:如果将阵列大小设置为1100,文件将非常大:刚好超过1 GB。显然,如果引发异常,文件不会被写入。C#无法支持超过1.5 GB的连续内存分配,即使序列化数据小于1 GB,反序列化时也可能接近1.5 GB。还有一个提示:如果将数组大小设置为1100,则可以在程序运行时观察文件的增长。如果将其设置为1101,则在引发异常之前,文件大小保持不变(0)。文件未刷新到磁盘可能有问题吗?在未分配内存的情况下运行程序时,这也会立即引发:
byte[]data=new byte[2147483648]内存不应该被使用,在
for
循环中的
产生返回应该注意这一点。此外,如果这是问题所在,那么无论我将数组的大小设置为1100还是1101,我都会看到相同的问题,不是吗?此外,当抛出异常时,我只使用了大约300MB的内存(至少根据任务管理器的说法)。@markmuetz异常发生在哪一行?此外,我并不关心您是否
产生返回,而是关心实际分配了多少连续内存。如果应用程序中的某个地方分配了1.5 GB的内存,则会出现内存不足异常。@markmuetz这绝对是一个有趣的问题,我不确定那里还会发生什么。然而,我想解决的主要问题是,在某个时刻,您的应用程序试图利用1.5 GB的连续空间。该死的bug悄悄地进来了!您对该症状的分析是正确的,但没有理由(除了胖手指)需要这样做-请参阅我的答案以获取更新。修复效果非常好,非常感谢。我会在有时间的时候试用新版本。@markmuetz我可能很快就会发布,因为我最近增加了对phone 8的支持