2

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

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

2 回答 2

0

尝试如下更改while语句。我没有对此进行测试,但它与原始代码的不同之处在于每次while迭代都会处理XmlReader.

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

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 页面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

于 2013-03-07T00:04:50.333 回答
0

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

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

于 2013-03-08T03:33:30.923 回答