【问题标题】:OutOfMemoryException when reading a string读取字符串时出现 OutOfMemoryException
【发布时间】:2016-03-29 22:55:08
【问题描述】:

我有一个 C# .NET 系统,它接受 JSON 数据馈送并使用 Newtonsoft.Json.JsonConvert.DeserializeObject 转换器将其转换为对象。

只要 JSON 字符串小于一定大小(几 Mb​​),这个过程就可以完美运行,但是一旦返回的数据很大(几乎 100Mb),我就会收到错误 OutOfMemoryException

此代码适用于小数据:

// WebClient ------------------------------------------------------------------
var _client = new System.Net.WebClient();
var _content = _client.DownloadString(_url);

但在最后一行炸毁了 (DownloadString)

我尝试更改为这也适用于小数据,但当数据大小增加时,它仍然在ReadToEnd 行上爆炸。

using (var _response = (System.Net.HttpWebResponse)_request.GetResponse())
{
    using (System.IO.Stream _dataStream = _response.GetResponseStream())
    {
        using (System.IO.StreamReader _streamReader = new System.IO.StreamReader(_dataStream))
        {
            string _responseFromServer = _streamReader.ReadToEnd();
        }
    }
}

最后我尝试了这个方法:

StringBuilder _stringBuilder = new StringBuilder();
using (var _response = (System.Net.HttpWebResponse)_request.GetResponse())
{
    using (System.IO.Stream _dataStream = _response.GetResponseStream())
    {
        using (System.IO.StreamReader _streamReader = new System.IO.StreamReader(_dataStream))
        {
            while (!streamReader.EndOfStream)
            {
                char[] _buffer = new char[4096];
                _streamReader.ReadBlock(_buffer, 0, _buffer.Length);
                var _bufferString = new String(_buffer);
                _stringBuilder.Append(_bufferString);
            }
        }
    }
}

但是当它到达下一行时,它会因OutOfMemoryException 错误而爆炸:

var _results = Newtonsoft.Json.JsonConvert.DeserializeObject<List<MyObject>>(_stringBuilder.ToString());

它不喜欢ToString() 方法。

它也因像

这样的简单行而崩溃
string _convertedString = _stringBuilder.ToString();

完整的错误是:

“System.OutOfMemoryException”类型的异常发生在 mscorlib.dll 但未在用户代码中处理

机器运行 64 位 Windows 和 16Gb 内存。

那么,我有哪些选择?

我想要的只是来自(非常大的)JSON 字符串的IQueryable&lt;MyObject&gt;

【问题讨论】:

  • 查看这个问题的答案,stackoverflow.com/questions/27315521/…,一次反序列化一个对象
  • OutOfMemoryException 可能由于各种原因而发生,例如耗尽缓冲区、GDI 对象等。异常的调用堆栈会告诉您发生的位置,但真正的罪魁祸首是代码效率低下。 为什么当您可以通过 StreamReader 读取数据时使用 StringBuilder?您所做的并不比streamReader.ReadToEnd() 更好(或更高效)。
  • @PanagiotisKanavos - 这些只是我试图让它工作的各种方法,只是为了展示这些过程。如果有办法在没有内存错误的情况下将 StreamReader 中的值放入 Deserializer,那么我很想知道。
  • 如果您创建a minimal, complete and verifiable example,我们会更容易看到问题。
  • @dbugger - 我没看到那个。从来不知道你可以一次反序列化一个项目。我会试一试,看看是否可行。谢谢

标签: c# json rest asp.net-web-api json.net


【解决方案1】:

您的代码基本上模拟了StreamReader.ReadToEnd 所做的事情,至少占用了读取大型响应所需内存的 4 倍(字符串响应本身的内存、StringBuilder 的内部缓冲区、所有中间临时字符串的大小和最后的字符串)。

您可以通过直接使用 JsonTextReader 从流中反序列化来避免这种情况。复制自documentation sample

using (var json= new JsonTextReader(streamReader))
{
    JsonSerializer serializer = new JsonSerializer();
    return (List<MyObject>)serializer.Deserialize(json, typeof(List<MyObject>));
}

【讨论】:

  • 完美!第一次工作 - 只是对您的代码进行了微小的更改 - 将 word 文件更改为 jsonTextReader
猜你喜欢
  • 1970-01-01
  • 2012-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多