为什么更改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);
}
}
}
存在内存泄漏,因为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
里面有什么