我创建了一个PowerShell脚本,它循环处理大量XML (.xsd)文件,并为每个脚本创建一个.NET XmlSchemaSet对象,调用Add()和Compile()向其添加模式,并打印出所有验证错误。
这个脚本工作正常,但是在某个地方有一个内存泄漏,如果在1000多个文件上运行,会导致它消耗千兆字节的内存。
本质上,我在循环中所做的工作如下:
$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我还试着从$schema上拿起Add()
[void] $schemaSet.RemoveRecursive($schema)这些似乎有一些效果,但仍然有漏洞。我假定旧的XmlSchemaSet实例仍然在使用内存而不被垃圾收集。
这个问题:如何正确地教导垃圾收集器它可以回收上面代码中使用的所有内存?或者更广泛地说:我如何用有限的内存来实现我的目标?
发布于 2012-07-16 15:38:33
微软已经确认这是PowerShell 2.0中的一个bug,他们说这在PowerShell 3.0中已经解决了。
问题是使用registered注册的事件处理程序不是垃圾收集的。微软在回应支持电话时说,
“我们正在处理PowerShell v.2中的一个bug。这个问题实际上是由于.NET对象实例由于事件处理程序本身未被释放而不再释放。这个问题在PowerShell v.3中不再是可复制的。”
据我所见,最好的解决方案是在不同的级别上在PowerShell和.NET之间进行接口:完全用C#代码(嵌入在PowerShell脚本中)进行验证,然后返回一个ValidationEventArgs对象列表。请参阅https://gist.github.com/3697081上的固定再现脚本:该脚本在功能上是正确的,并且没有泄漏内存。
(感谢Microsoft支持我找到这个解决方案。)
最初,微软提供了另一种解决方案,即使用$xyzzy = Register-ObjectEvent -SourceIdentifier XYZZY,最后执行以下操作:
Unregister-Event XYZZY
Remove-Job $xyzzy -Force但是,这个解决方案在功能上是不正确的。在执行这两个附加语句时,仍处于“飞行中”的任何事件都会丢失。在我的例子中,这意味着我错过了验证错误,因此脚本的输出是不完整的。
发布于 2012-06-27 09:24:14
在remove-variable之后,您可以尝试强制GC集合:
[GC]::Collect()https://stackoverflow.com/questions/11222961
复制相似问题