【发布时间】: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