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

标签 c# json serialization json.net

我正在使用 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());
        }
    }

附言有些人可能会问为什么我要返回一个流而不是简单地序列化到文件流。这是因为我想在一个类中保留序列化,在另一个类中保留文件处理,所以稍后我将内存流传递给另一个类中的 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 字节数组的最坏情况。在调整大小过程中丢弃的旧字节数组也可能导致 Memory Fragmentation根据它们的生命周期,这很容易让您的程序在达到 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 时不关闭底层流。

关于c# - 序列化到 MemoryStream 会导致 OutOfmemoryException,但序列化到 FileStream 不会。谁能告诉我为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31796352/

相关文章:

c# - 构造函数中 C# 记录上的 JsonProperty

json - ASP.NET Web Api 和部分响应

java - 将 JSON 转换为 Java 对象时的 Setter 问题

java - 如何在 Retrofit2 Android 中动态重命名 JSON 响应的键

c# - Entity Framework : Add child record to and existing parent

c# - 如何使用 .NET 读取 Excel 文件 (.xls) 的二进制数据?

c# - 具有完整 CSS 支持的开源 HTML 到 PDF 渲染器

java - 添加到 ArrayList 后出现 NotSerializedException

php - 将 PHP 对象图序列化/反序列化为 JSON

Java序列化并立即反序列化出现错误