为什么更改XML模式并重新验证会导致内存泄漏

本文关键字:验证 泄漏 内存 XML 模式 新验证 为什么 | 更新日期: 2023-09-27 18:02:19

我最近在我维护的一个应用程序中发现了一个内存泄漏,我很困惑为什么代码会产生泄漏。我已经提取出相关的代码(略有修改),并提供如下。

在我们的应用程序中,给定的XML文档可以针对一个或多个可用的模式文件进行验证。随着时间的推移,每个模式文件对应于XML文档的不同版本。我们只关心XML文档针对至少一个模式进行验证。每个模式都完整地描述了XML文档的内容(它们不是嵌套的模式文件)。

根据ANTS内存分析器,看起来XmlDocument对象保存着对以前模式的引用,即使在模式集被清除之后也是如此。注释掉Validate()的调用,保持其他所有内容不变,将阻止泄漏。

我修复了应用程序中的漏洞,方法是在应用程序初始化时加载一次模式,并交换与XML文档关联的模式文件,直到找到一个验证的模式文件。

下面的代码产生内存泄漏,我不确定为什么。

class Program
{
    private static XmlDocument xmlDocument_ = new XmlDocument();
    static void Main(string[] args)
    {
        using (StreamReader reader = new StreamReader("contents.xml"))
        {
            xmlDocument_.LoadXml(reader.ReadToEnd());
        }
        XmlReaderSettings xmlReaderSettings = new XmlReaderSettings();
        xmlReaderSettings.CloseInput = true;
        while (true)
        {
            xmlDocument_.Schemas = new XmlSchemaSet();
            XmlReader xmlReader = XmlReader.Create("schema.xsd", xmlReaderSettings);
            xmlDocument_.Schemas.Add(XmlSchema.Read(xmlReader, null));
            xmlReader.Close();
            xmlDocument_.Validate(null);
        }
    }
}

为什么更改XML模式并重新验证会导致内存泄漏

存在内存泄漏,因为XmlDocument引用是静态的,并且SchemaInfo属性在验证XML时填充。由于这些属性保存了对编译后的xsd对象的引用,因此只要XmlDocument存在,这些属性就会存在,这可能需要相当长的一段时间(因为它是静态的)。

有些人可能会争论这是否确实是泄漏:用另一组xsd验证另一个XML将释放先前持有的资源。

尝试像下面这样更改while语句。我还没有测试过,但它与原始代码不同,因为每个while迭代都要处理XmlReader

GC最终可能会自动处理XmlReader实例,但我对此表示怀疑,因为XmlReader实现了IDispose。也就是说,使用XmlReader的代码必须确定地处理它(垃圾收集是非确定的)。如果GC能够处理它们,并且如果while在GC执行此操作之前迭代了数千次,那么所使用的内存无论如何都会杀死系统。

while (true)
{
    xmlDocument_.Schemas = new XmlSchemaSet();
    using (XmlReader xmlReader = XmlReader.Create("schema.xsd", xmlReaderSettings))
    {
        xmlDocument_.Schemas.Add(XmlSchema.Read(xmlReader, null));
    }
    xmlDocument_.Validate(null);
}
编辑:

我阅读了XmlDocument.Validate上的MSDN页面,它提供了一个不同的代码示例,使用XmlReaderSettings设置验证选项。此外,OP中的代码假定XML文件总是被编码为UTF-8。下面是基于MSDN样本检测文本编码的重写;这个可以修复内存泄漏。此代码未经测试。

class Program
{
    private static XmlDocument xmlDocument_ = new XmlDocument();
    static void Main(string[] args)
    {
        XmlReaderSettings settings = new XmlReaderSettings();
        settings.ValidationType = ValidationType.Schema;
        settings.CloseInput = true;
        xmlDocument_.Load(XmlReader.Create("contents.xml", settings));
        while (true)
        {
            settings.Schemas = new XmlSchemaSet();
            settings.Schemas.Add(null, "schema.xsd");
            xmlDocument_.Validate(null);
        }
    }
}

你可以试试ILDASM看看XmlDocument.Validate里面有什么