【问题标题】:Deserializing large files using Json.NET使用 Json.NET 反序列化大文件
【发布时间】:2017-08-11 11:11:14
【问题描述】:

我正在尝试处理大量数据(约 1000 个单独的文件,每个文件约 30 MB),以便用作机器学习算法训练阶段的输入。使用 JSON 格式化的原始数据文件,我使用 Json.NET 的 JsonSerializer 类对它们进行反序列化。在程序快结束时,Newtonsoft.Json.dll 抛出 'OutOfMemoryException' 错误。有没有办法减少内存中的数据,或者我必须改变我的所有方法(例如切换到像 Spark 这样的大数据框架)来处理这个问题?

public static List<T> DeserializeJsonFiles<T>(string path)
{
    if (string.IsNullOrWhiteSpace(path))
        return null;

    var jsonObjects = new List<T>();
    //var sw = new Stopwatch();
    try
    {
        //sw.Start();
        foreach (var filename in Directory.GetFiles(path))
        {
            using (var streamReader = new StreamReader(filename))
            using (var jsonReader = new JsonTextReader(streamReader))
            {
                jsonReader.SupportMultipleContent = true;
                var serializer = new JsonSerializer();

                while (jsonReader.Read())
                {
                    if (jsonReader.TokenType != JsonToken.StartObject)
                        continue;

                    var jsonObject = serializer.Deserialize<dynamic>(jsonReader);

                    var reducedObject = ApplyFiltering(jsonObject) //return null if the filtering conditions are not met 
                    if (reducedObject == null)
                        continue;

                    jsonObject = reducedObject;
                    jsonObjects.Add(jsonObject);
                }
            }
        }    
        //sw.Stop();
        //Console.WriteLine($"Elapsed time: {sw.Elapsed}, Elapsed mili: {sw.ElapsedMilliseconds}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error: {ex}")
        return null;
    }

    return jsonObjects;
}

谢谢。

【问题讨论】:

  • 嗯,你并没有真正使用 JsonTextReader。您正在使用 JsonSerializer 并且 也将所有内容都读取到内存中。您可以切换到 64 位以避免 OOM,但无论如何您的工作集都会非常大。您的问题不在于 JSON,而在于您保留了很多巨型对象(GC 也不特别喜欢这些对象)。
  • 正如乔伊所说,您最好通过流式传输数据。与其将所有的 ML 都一次性投入其中,不如将其逐一灌输。
  • @Joey,已经是 64 位了。我会尝试蒂姆的方法。顺便说一下,我纠正了误导的部分,谢谢。
  • 我同意其他人的看法;您应该在从流中获取每个项目时单独处理它们,而不是将它们全部添加到列表中,这会将所有内容都保存在内存中。如果您必须将内容添加到列表中,请尝试使用链表之类的东西,它不会在一个连续的块中分配内存,并且在需要扩展其容量时不需要重新分配和复制所有内容。
  • 我按照 Tim 的建议使用了收益回报。此外,在 ApplyFilter() 方法的开头,我在过滤每个反序列化项目之前创建了一个自定义模型的新实例。我已修复此问题以首先处理反序列化数据,然后仅在满足所有过滤条件时才将其映射到我的自定义模型(不创建实例,否则返回 null)。堆大小大大减小。没想过用链表,我试试,谢谢。

标签: c# .net performance json.net


【解决方案1】:

这不是 Newtonsoft 的真正问题。您正在将所有这些对象读入内存中的一个大列表中。它达到了您要求JsonSerializer 创建另一个对象的地步,但它失败了。

您需要从您的方法中返回IEnumerable&lt;T&gt;yield return 每个对象,并在调用代码中处理它们而不将它们存储在内存中。这意味着迭代IEnumerable&lt;T&gt;,处理每个项目,然后写入磁盘或任何需要结束的地方。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-27
    • 2012-04-09
    • 1970-01-01
    相关资源
    最近更新 更多