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

时间:2013-03-05 21:45:38

标签: c# xml memory-leaks xsd

我最近在工作中维护的应用程序中发现了内存泄漏,我对代码产生泄漏的原因感到困惑。我已经提取相关代码(稍作修改)并在下面提供。

在我们的应用程序中,给定的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);
        }
    }
}

2 个答案:

答案 0 :(得分:0)

尝试更改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);
}

修改

我阅读了MSDN page on XmlDocument.Validate,它使用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内的内容。

答案 1 :(得分:0)

您有内存泄漏,因为您的XmlDocument引用是静态的,并且由于SchemaInfo属性,在您验证XML时会填充该属性。由于这些属性包含对已编译的XSD中对象的引用,因此只要您拥有XmlDocument,就可以使用这些对象,这可能需要一段时间(因为它是静态的)。

有些人可能会争论是否确实存在泄漏:使用另一组XSD验证另一个XML将释放以前保存的资源。