【问题标题】:`System.Xml.Linq` consumes a massive amount of memory`System.Xml.Linq` 消耗大量内存
【发布时间】:2022-01-16 07:53:18
【问题描述】:

看起来System.Xml.Linq 正在消耗大量内存,即使在任何资源应该被释放之后也是如此。 一个简单的演示

await using ( System.IO.FileStream stream = new ( xmlFilePath, System.IO.FileMode.Open) ) {
    using ( System.Xml.XmlReader reader = System.Xml.XmlReader.Create( stream, new () { ConformanceLevel = System.Xml.ConformanceLevel.Fragment, Async = true } ) ) {
        int i = 0;
        while ( await reader.ReadAsync().ConfigureAwait( false ) ) {
            while ( reader.NodeType != System.Xml.XmlNodeType.None ) {
                if ( reader.NodeType == System.Xml.XmlNodeType.XmlDeclaration ) {
                    await reader.SkipAsync().ConfigureAwait( false );
                    continue;
                }
                if ( ct.IsCancellationRequested ) {
                    continue;
                }
                i++;
                if ( i % 100000 == 0 ) {
                    Console.WriteLine( $"Processed {i}: {reader.ReadString()}" );
                }
                System.Xml.Linq.XNode node = await System.Xml.Linq.XNode.ReadFromAsync( reader, ct ).ConfigureAwait( false );

            }
        }
    }
}
Console.WriteLine( $"\n---->Memory Use/false: {GC.GetTotalMemory(false):N0}");
Console.WriteLine( $"---->Memory Use      : {GC.GetTotalMemory(true):N0}\n");
return;

输出:

---->Memory Use/false: 402,639,448
---->Memory Use      : 400,967,152

如果我替换 XNode 部分,

                        string xmlFilePath = "/home/eric/dev/src/github.com/erichiller/mkmrk-dotnet/src/Cli/dataset/cme/definition/2021/11/2021-11-05/20211104.061134-05_20211104.030927-05_cmeg.nymex.fut.prf.xml";
                        
await using ( System.IO.FileStream stream = new ( xmlFilePath, System.IO.FileMode.Open) ) {
    using ( System.Xml.XmlReader reader = System.Xml.XmlReader.Create( stream, new () { ConformanceLevel = System.Xml.ConformanceLevel.Fragment, Async = true } ) ) {
        int i = 0;
        while ( await reader.ReadAsync().ConfigureAwait( false ) ) {
            while ( reader.NodeType != System.Xml.XmlNodeType.None ) {
                if ( reader.NodeType == System.Xml.XmlNodeType.XmlDeclaration ) {
                    await reader.SkipAsync().ConfigureAwait( false );
                    continue;
                }
                if ( ct.IsCancellationRequested ) {
                    continue;
                }
                i++;
                if ( i % 100000 == 0 ) {
                    Console.WriteLine( $"Processed {i}: {reader.ReadString()}" );
                }
                await reader.ReadAsync().ConfigureAwait( false );
            }
        }
    }
}
Console.WriteLine( $"\n---->Memory Use/false: {GC.GetTotalMemory(false):N0}");
Console.WriteLine( $"---->Memory Use      : {GC.GetTotalMemory(true):N0}\n");
return;

使用量大幅下降:

---->Memory Use/false: 11,048,992
---->Memory Use      : 6,317,248

我在这里误解了什么/做错了什么?正在加载的文件很大(~60MB),但即使 XNode 需要使用这么多内存,不应该在到达Console.WriteLine 时释放它吗?

【问题讨论】:

  • 不 - 它的不确定性 - .net 是 gc'd,一旦块关闭,事情并不总是从堆中释放
  • 出于好奇,你为什么不叫break;而不是继续;取消令牌何时取消?
  • 我最终重写为直接使用 XmlReader 而不是通过 System.Xml.Linq ;更好的性能和内存消耗 (~40MB)

标签: c# .net xml linq .net-core


【解决方案1】:

Linq to XML 急切地将整个 XML 文档加载到内存中,并创建许多对象来表示它。您似乎在阅读时循环执行了很多次 - 没有太多保护递归遍历。

但是,使用XmlReader,它允许手动控制,它只读取足够的信息并允许消费者决定如何处理它。

关闭块时并不总是释放内存。堆上的东西会在某个时候被 GC 清理掉。

【讨论】:

  • 但是 OP 使用参数 forceFullCollection = true 调用 GetTotalMemory 并且 GC 没有按照 OP 的要求进行清理
  • @RandRandom true 但可能有很多碎片,这是 gc 工作原理的实现细节
  • @RandRandom 你需要在保存数据的函数外部做GC,否则GC可能不会释放它
  • @Charlieface 将 XML 代码放在执行 GC 的方法之外不会改变结果。我试着确定一下。
  • 但据我了解,如果 OP 在代码执行后有有效的方法来释放内存,OP 不介意内存消耗,目前您只回答了问题的一部分,这两种方法有什么区别,但不是为什么内存没有被释放
猜你喜欢
  • 2014-01-04
  • 2012-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多