11

我创建了一个 PowerShell 脚本,它遍历大量 XML Schema (.xsd) 文件,并为每个文件创建一个 .NETXmlSchemaSet对象,调用Add()并向Compile()其添加架构,并打印出所有验证错误。

该脚本可以正常工作,但是某处存在内存泄漏,如果在 100 多个文件上运行,它会消耗千兆字节的内存。

我基本上在一个循环中做的是以下内容:

$schemaSet = new-object -typename System.Xml.Schema.XmlSchemaSet
register-objectevent $schemaSet ValidationEventHandler -Action {
    ...write-host the event details...
}
$reader = [System.Xml.XmlReader]::Create($schemaFileName)
[void] $schemaSet.Add($null_for_dotnet_string, $reader)
$reader.Close()
$schemaSet.Compile()

(可以在此要点中找到重现此问题的完整脚本:https ://gist.github.com/3002649 。只需运行它,然后在任务管理器或进程资源管理器中观察内存使用量的增加。)

受一些博客文章的启发,我尝试添加

remove-variable reader, schemaSet

我也试着拿起$schemaAdd()

[void] $schemaSet.RemoveRecursive($schema)

这些似乎有一些效果,但仍然存在泄漏。我假设较旧的实例XmlSchemaSet仍在使用内存而没有被垃圾收集。

问题:我如何正确地教垃圾收集器它可以回收上面代码中使用的所有内存?或者更笼统地说:我怎样才能用有限的内存来实现我的目标?

4

2 回答 2

9

Microsoft 已确认这是 PowerShell 2.0 中的一个错误,并且他们声明这已在 PowerShell 3.0 中得到解决。

问题是使用 Register-ObjectEvent 注册的事件处理程序没有被垃圾收集。在回复支持电话时,微软表示

“我们正在处理 PowerShell v.2 中的一个错误。这个问题实际上是由 .NET 对象实例由于事件处理程序本身没有被释放而不再被释放的事实引起的。这个问题不再可以通过 PowerShell 重现v.3"。

据我所知,最好的解决方案是在不同级别的 PowerShell 和 .NET 之间进行接口:完全在 C# 代码中进行验证(嵌入在 PowerShell 脚本中),然后只传回ValidationEventArgs对象列表。请参阅https://gist.github.com/3697081上的固定复制脚本:该脚本在功能上是正确的并且不会泄漏任何内存。

(感谢 Microsoft 支持帮助我找到此解决方案。)


最初,Microsoft 提供了另一种解决方法,即使用$xyzzy = Register-ObjectEvent -SourceIdentifier XYZZY,然后在最后执行以下操作:

Unregister-Event XYZZY
Remove-Job $xyzzy -Force

但是,此解决方法在功能上不正确。在执行这两个附加语句时,任何仍在“进行中”的事件都会丢失。就我而言,这意味着我错过了验证错误,因此我的脚本输出不完整。

于 2012-07-16T15:38:33.537 回答
4

之后remove-variable你可以尝试强制 GC 收集:

[GC]::Collect()
于 2012-06-27T09:24:14.883 回答