繁体   English   中英

序列化到MemoryStream会导致OutOfmemoryException,但序列化到FileStream却不会。 谁能告诉我为什么?

[英]Serializing to MemoryStream causes an OutOfmemoryException, but serializing to a FileStream does not. Can anyone tell me why?

我正在使用Newtonsoft Json.Net将对象序列化为json。 当我尝试序列化为MemoryStream时 ,我不断遇到OutOfMemoryException ,但是当我序列化为FileStream时却没有。 有人可以解释为什么会这样吗? 这是我用来序列化的两种方法。

引发OutOfMemoryException

        private static MemoryStream _serializeJson<T>(T obj)
    {
        try
        {
            var stream = new MemoryStream();
            var streamWriter = new StreamWriter(stream);
            var jsonWriter = new JsonTextWriter(streamWriter);
            var serializer = new JsonSerializer();
            serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
            serializer.Formatting = Formatting.Indented;
            serializer.Serialize(jsonWriter, obj);
            streamWriter.Flush();
            stream.Position = 0;
            return stream;
        }
        catch (Exception e)
        {
            //Logger.WriteError(e.ToString());
            Console.WriteLine(e.ToString());
            return null;
        }
    }

不抛出OutOfMemoryException

    private static void _serializeJsonToFile<T>(T obj, string path)
    {
        try
        {
            using (FileStream fs = File.Open(path, FileMode.Create, FileAccess.ReadWrite))
            using (StreamWriter sw = new StreamWriter(fs))
            using (JsonWriter jw = new JsonTextWriter(sw))
            {
                jw.Formatting = Formatting.Indented;
                JsonSerializer serializer = new JsonSerializer();
                serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
                serializer.Serialize(jw, obj);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

PS有些人可能会问为什么我要返回流而不是简单地序列化到文件流。 这是因为我想将序列化保留在一个类中,而将文件处理保留在另一类中,所以我稍后将内存流传递给另一类中的WriteFile方法。

之所以会收到OutOfMemoryExceptions,是因为内存流对其增长非常积极。 每次需要调整大小时,它都会将内部缓冲区加倍。

//The code from MemoryStream http://referencesource.microsoft.com/mscorlib/system/io/memorystream.cs.html#1416df83d2368912
private bool EnsureCapacity(int value) {
    // Check for overflow
    if (value < 0)
        throw new IOException(Environment.GetResourceString("IO.IO_StreamTooLong"));
    if (value > _capacity) {
        int newCapacity = value;
        if (newCapacity < 256)
            newCapacity = 256;
        // We are ok with this overflowing since the next statement will deal
        // with the cases where _capacity*2 overflows.
        if (newCapacity < _capacity * 2)
            newCapacity = _capacity * 2;
        // We want to expand the array up to Array.MaxArrayLengthOneDimensional
        // And we want to give the user the value that they asked for
        if ((uint)(_capacity * 2) > Array.MaxByteArrayLength)
            newCapacity = value > Array.MaxByteArrayLength ? value : Array.MaxByteArrayLength;

        Capacity = newCapacity;
        return true;
    }
    return false;
}

使用17.8 MB文件,这是使用35.6 MB字节数组的最坏情况。 在调整大小的过程中丢弃的旧字节数组也会根据其生存时间而导致内存碎片 ,这很容易使程序在达到32位内存限制之前引发OOM错误。

直接写入FileStream不需要在内存中创建任何大缓冲区,因此它使用的空间要少得多。

有一种方法可以将保存的逻辑与序列化分开,只需将流传递给函数,而不是在函数本身中创建它。

private static void _serializeJson<T>(T obj, Stream stream)
{
    try
    {
        using(var streamWriter = new StreamWriter(stream, Encoding.UTF8, 1024, true))
        using(var jsonWriter = new JsonTextWriter(streamWriter))
        {
            var serializer = new JsonSerializer();
            serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
            serializer.Formatting = Formatting.Indented;
            serializer.Serialize(jsonWriter, obj);
         }
    }
    catch (Exception e)
    {
        //Logger.WriteError(e.ToString());
        Console.WriteLine(e.ToString());
    }
}

我还处置了创建的StreamWriter,我使用的构造函数有一个leaveOpen标志,当您处置StreamWriter时,该标志导致基础流不会关闭。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM