解压缩非常大的序列化对象并管理内存

时间:2010-06-11 21:12:43

标签: c# .net serialization

我有一个包含大量用于报告的数据的对象。为了从服务器到客户端获取此对象,我首先在内存流中序列化对象,然后使用.NET的Gzip流压缩它。然后我将压缩对象作为byte []发送到客户端。

问题在于某些客户端,当他们获取byte []并尝试解压缩和反序列化对象时,会抛出System.OutOfMemory异常。我已经读过这个异常可能是由new()引起的一堆对象引起的,或者是一堆字符串引起的。这两种情况都发生在反序列化过程中。

所以我的问题是:如何防止异常(任何好的策略)?客户端需要所有数据,并尽可能地减少了字符串的数量。

编辑:这是我用来序列化/压缩的代码(实现为扩展方法)

public static byte[] SerializeObject<T>(this object obj, T serializer) where T: XmlObjectSerializer
{
    Type t = obj.GetType();

    if (!Attribute.IsDefined(t, typeof(DataContractAttribute)))
        return null;

    byte[] initialBytes;

    using (MemoryStream stream = new MemoryStream())
    {
        serializer.WriteObject(stream, obj);
        initialBytes = stream.ToArray();
    }

    return initialBytes;
}

public static byte[] CompressObject<T>(this object obj, T serializer) where T : XmlObjectSerializer
{
    Type t = obj.GetType();

    if(!Attribute.IsDefined(t, typeof(DataContractAttribute)))
        return null;

    byte[] initialBytes = obj.SerializeObject(serializer);

    byte[] compressedBytes;

    using (MemoryStream stream = new MemoryStream(initialBytes))
    {
        using (MemoryStream output = new MemoryStream())
        {
            using (GZipStream zipper = new GZipStream(output, CompressionMode.Compress))
            {
                Pump(stream, zipper);
            }

            compressedBytes = output.ToArray();
        }
    }

    return compressedBytes;
}

internal static void Pump(Stream input, Stream output)
{
    byte[] bytes = new byte[4096];
    int n;
    while ((n = input.Read(bytes, 0, bytes.Length)) != 0)
    {
        output.Write(bytes, 0, n);
    }
}

这是我的解压缩/反序列化代码:

public static T DeSerializeObject<T,TU>(this byte[] serializedObject, TU deserializer) where TU: XmlObjectSerializer
{
    using (MemoryStream stream = new MemoryStream(serializedObject))
    {
        return (T)deserializer.ReadObject(stream);
    }
}

public static T DecompressObject<T, TU>(this byte[] compressedBytes, TU deserializer) where TU: XmlObjectSerializer
{
    byte[] decompressedBytes;

    using(MemoryStream stream = new MemoryStream(compressedBytes))
    {
        using(MemoryStream output = new MemoryStream())
        {
            using(GZipStream zipper = new GZipStream(stream, CompressionMode.Decompress))
            {
                ObjectExtensions.Pump(zipper, output);
            }

            decompressedBytes = output.ToArray();
        }
    }

    return decompressedBytes.DeSerializeObject<T, TU>(deserializer);
}

我传递的对象是一个包装器对象,它只包含保存数据的所有相关对象。对象的数量可以很多(取决于报告的日期范围),但我已经看到多达25k个字符串。

我忘记提到的一件事是我正在使用WCF,并且由于内部对象通过其他WCF调用单独传递,我使用的是DataContract序列化程序,并且所有对象都使用DataContract属性进行标记。

2 个答案:

答案 0 :(得分:0)

如果您还没有这样做,可以在客户端尝试pre-generating the XmlSerializer assemblies

.NET实际上是在运行时生成这些内容,除非您预先生成并链接它们。

更多:Sgen.exe以及StackOverflow上的更多内容。

答案 1 :(得分:0)

我工作的开发人员遇到了类似的问题,其中用于序列化的大型流碎片化了内存堆,垃圾收集器无法将其压缩到足以允许他重新分配内存。

如果要重复序列化多个对象,我会分配一个缓冲区,然后在每次完成时清除它,而不是处理它并创建一个新缓冲区。这样你只需要内存来创建一次,然后你的应用程序应该继续有效地工作。

我还提到@yetapb评论说可能会以流式方式分页和编写数据。这样你就不需要内存中的巨大缓冲区来存储数据。