使用DataContractSerializer序列化对象后调用MemoryStream.ToArray()时出现System.OutOfMemory异常

时间:2011-07-14 21:48:40

标签: c# .net datacontractserializer memorystream

我在这句话中得到了间歇性的“内存不足”异常:

        return ms.ToArray();

在这种方法中:

public static byte[] Serialize(Object inst)
{
    Type t = inst.GetType();
    DataContractSerializer dcs = new DataContractSerializer(t);
    MemoryStream ms = new MemoryStream();
    dcs.WriteObject(ms, inst);
    return ms.ToArray();
}

我该如何预防呢?有更好的方法吗?

ms的长度是182,870,206字节(174.4 MB)

我将它放入一个字节数组,以便我可以通过压缩运行它并将其存储到磁盘。数据(显然)是我在Silverlight应用程序启动时从WCF服务器下载的自定义类的大型列表。我正在对它进行序列化并对其进行压缩,因此它在隔离存储中仅使用大约6MB。下次用户从Web访问并运行silverlight应用程序时,我会检查时间戳,如果好,我只是从隔离打开文件,解压缩,反序列化并加载我的结构。我将整个结构保留在内存中,因为应用程序主要是围绕操作此结构的内容。

@configurator是正确的。阵列的大小太大了。我通过自己的序列化程序,通过声明[list record count *每个记录的字节数]的字节数组,然后使用这样的语句直接填充它来填充它:

    Buffer.BlockCopy(
           BitConverter.GetBytes(node.myInt),0,destinationArray,offset,sizeof(int)); 
    offset += sizeof(int);

这是为了取回它:

    newNode.myInt= BitConverter.ToInt32(sourceByteArray,offset); 
    offset += sizeof(int);

然后我将其压缩并存储到隔离存储中。

我的尺寸从DataBontractSerializer的174MB变为我的14MB。 压缩后,它在隔离存储中从6MB传输到1MB文件。

感谢Configurator和Filip的帮助。

4 个答案:

答案 0 :(得分:3)

问题似乎是你期望返回180MB字节数组。这意味着框架需要找到并分配一个连续的180MB可用内存来复制流数据,这通常很难 - 因此OutOfMemoryException。如果需要继续处理这么大的内存,请使用内存流本身(根据需要读取和写入)来保存缓冲区;否则,将其保存到文件(或者您需要的其他任何地方,例如通过网络提供),而不是使用内存流。

我应该提一下,内存流也有自己的180MB阵列,所以也有点麻烦,并且在序列化过程中可能会导致OutOfMemory - 它可能会更好(如更强大)您可以将其序列化为temporary file。您可能还需要考虑更紧凑但可能更不易读的序列化格式,如json,二进制序列化或协议缓冲区。


在回复评论时:要直接序列化到磁盘,请使用FileStream代替MemoryStream

public static void Serialize(Object inst, string filename)
{
    Type t = inst.GetType();
    DataContractSerializer dcs = new DataContractSerializer(t);
    using (FileStream stream = File.OpenWrite(filename)) {
        dcs.WriteObject(ms, inst);
    }
}

答案 1 :(得分:0)

我不知道你是如何使用该代码的,但有一点让我感到震惊的是你没有释放你的资源。例如,如果您使用大量大型对象多次调用Serialize(obj),则最终会使用大量未直接释放的内存,但GC应正确处理,但你应该总是释放你的资源。

我尝试过这段代码:

public static byte[] Serialize(object obj)
{
    Type type = obj.GetType();
    DataContractSerializer dcs = new DataContractSerializer(type);

    using (var stream = new MemoryStream())
    {
        dcs.WriteObject(stream, obj);
        return stream.ToArray();
    }
}

在控制台应用程序中使用以下Main - 方法

static void Main(string[] args)
{
    var filipEkberg = new Person {Age = 24, Name = @"Filip Ekberg"};

    var obj = Serialize(filipEkberg);
}

然而,我的byte阵列并不像你的那么大。 Having a look at this similar issue,您可能需要考虑查看protobuf-net

了解您打算如何处理序列化数据可能也很有趣,您是否需要将其作为byte - 数组或者是否也可以将XML写入文本文件?< / p>

答案 2 :(得分:-1)

尝试序列化为流(即FileStream)而不是字节数组。这样,您可以在没有OutOfMemory异常的情况下序列化数十亿字节的数据。

        public static void Serialize<T>(T obj, string path)
        {
            DataContractSerializer serializer = new DataContractSerializer(typeof(T));
            Stream stream = File.OpenWrite(path);
            serializer.WriteObject(stream, obj);
        }

        public static T Deserialize<T>(string path)
        {
            DataContractSerializer serializer = new DataContractSerializer(typeof(T));
            Stream stream = File.OpenRead(path);
            return (T)serializer.ReadObject(stream);
        }

答案 3 :(得分:-4)

尝试将内存流位置设置为0,然后仅调用ToArray()。

问候。