我创建了一个PowerShell脚本,该脚本循环访问大量XML架构(.xsd)文件,并为每个文件创建一个.NET XmlSchemaSet
对象,调用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
我还尝试从Add()
那里拿起$schema
并做
[void] $schemaSet.RemoveRecursive($schema)
这些似乎有一些效果,但仍然存在泄漏。 我假设较旧的XmlSchemaSet
实例仍在使用内存而不会被垃圾回收。
问题:如何正确教导垃圾回收器它可以回收上述代码中使用的所有内存? 或者更一般地说:我怎样才能用有限的内存量来实现我的目标?
>Microsoft已经确认这是PowerShell 2.0中的一个错误,他们声明这已经在PowerShell 3.0中得到解决。
问题是使用 Register-ObjectEvent 注册的事件处理程序不会被垃圾回收。 在回应支持电话时,Microsoft说
"我们正在处理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
但是,此解决方法在功能上不正确。 在执行这两个附加语句时,任何仍在"进行中"的事件都将丢失。 就我而言,这意味着我错过了验证错误,因此脚本的输出不完整。
remove-variable
后,您可以尝试强制收集 GC :
[GC]::Collect()