我需要以流的方式反序列化一个 1GB json 文件,一次一个元素,经过一些阅读后发现
JsonSerializer.DeserializeAsyncEnumerable
可以启用此功能,同时还保持内存使用量较低。我认为这实际上意味着内存使用量的峰值不会真正超过集合中单个 json 对象的大小(对于其他进程可能会多一点)和/或大约 json 缓冲区的大小,默认情况下为 4MB(可调整) .
然而,在运行以下基准测试后
.NET 8
,结果并不是我所期望的 - 为什么在此过程中消耗了 2GB+ 内存 - 我是否严重误解了应该发生的事情😖?
[MemoryDiagnoser(true)]
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
[ShortRunJob]
public class DeserializationBenchmarks
{
[Benchmark]
public async Task StreamJsonAndDeserialize()
{
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var peopleJson = File.OpenRead("./peeps.json"); //1GB+
var people = JsonSerializer.DeserializeAsyncEnumerable<Person>(peopleJson, options);
await foreach (var person in people)
{
await Console.Out.WriteLineAsync(person.Name);
}
await peopleJson.DisposeAsync();
}
}
// The json file is just an array of this simple Person object
public class Person
{
public string Id { get; set; }
public string Name { get; set; }
public string Language { get; set; }
public string Bio { get; set; }
public double Version { get; set; }
}
我尝试过调整缓冲区大小,但没有运气,我还尝试在反序列化后将每个
person
对象清空,以确保它得到 GC(也许毫无意义) - 我希望以更多方式反序列化此文件高效记忆庄园
老实说我不能说我知道它发生的原因,我有猜测。 也许 GC 没有启动,因为它还没有看到它的必要性,我想知道如果你在内存较低的系统中运行它会发生什么?
我有2个解决方案:
您可以使用 Cinchoo ETL,https://github.com/Cinchoo/ChoETL 一个开源的、良好的 dotnet ETL 库。然后像这样使用它:
foreach (dynamic e in new ChoJSONReader("Emp.json"))
Console.WriteLine("Id: " + e.Id + " Name: " + e.Name);
(参考:https://github.com/Cinchoo/ChoETL/wiki/QuickJSONLoad)
如果这不适用于您的用例,如何批量读取和解析 json?