.NET 7中JsonDocument是否存在内存泄漏问题?
JSON解析后内存持续占用问题的排查与解决
问题分析
你的代码中内存从4660k增长到6920k后无法下降,核心原因有两点:
- 根引用未释放:循环外定义的
data变量持有整个JSON的字符串,该对象作为Main方法的局部变量,全程处于GC根引用中,内存无法被回收。 - 大对象堆(LOH)回收机制:若JSON文件较大,
data会被分配到LOH,LOH的自动回收触发条件远严格于普通小对象堆,默认的GC.Collect()不会强制回收LOH内存。
虽然你用using包裹了JsonDocument,确保其自身占用的内存被释放,但底层源字符串data的内存始终被持有,导致总内存无法下降。
解决方案
1. 推荐:使用流解析避免大对象内存占用
直接从文件流解析JSON,无需一次性将整个文件加载到内存,从根源上减少大对象的创建:
static async Task Main(string[] args) { Console.WriteLine("Begin parsing data..."); for (var i = 0; i < 100; i++) { using var stream = File.OpenRead("./generated.json"); using JsonDocument jsonDocument = await JsonDocument.ParseAsync(stream); Thread.Sleep(650); } Console.WriteLine("Batch task ends..."); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true); Console.ReadLine(); }
2. 解除根引用并强制回收LOH
如果必须保留一次性读取JSON的逻辑,可在循环结束后解除data的根引用,再强制GC回收所有代(包括LOH):
static void Main(string[] args) { string data = File.ReadAllText("./generated.json"); Console.WriteLine("Begin parsing data..."); for (var i = 0; i < 100; i++) { using (JsonDocument jsonDocument = JsonDocument.Parse(data)) { } Thread.Sleep(650); } Console.WriteLine("Batch task ends..."); data = null; // 解除对JSON字符串的根引用 // 强制回收所有代,包括大对象堆 GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); GC.WaitForPendingFinalizers(); // 等待终结器执行完成 Console.ReadLine(); }
3. 临时测试方案:将读取逻辑移至循环内(不推荐生产环境)
把JSON读取放到循环内部,每次循环结束后data的局部变量会被GC回收,但会增加文件IO开销:
static void Main(string[] args) { Console.WriteLine("Begin parsing data..."); for (var i = 0; i < 100; i++) { using var reader = File.OpenText("./generated.json"); using JsonDocument jsonDocument = JsonDocument.Parse(reader.ReadToEnd()); Thread.Sleep(650); } Console.WriteLine("Batch task ends..."); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true); Console.ReadLine(); }
内容的提问来源于stack exchange,提问作者pigLoveRabbit520
相关产品推荐
相关产品推荐

