2012-06-27 63 views
11

我創建了一個循環大量XML Schema(.xsd)文件的PowerShell腳本,併爲每個文件創建一個.NET XmlSchemaSet對象,並調用Add()Compile()向其添加架構,並打印出所有驗證錯誤。如何指示PowerShell垃圾收集.NET對象,如XmlSchemaSet?

此腳本可正常工作,但某處存在內存泄漏,導致它在數百個文件上運行時會消耗千兆字節的內存。

我基本上是做一個循環如下:

$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只要運行它,看在內存使用率增加任務管理器或Process Explorer)

一些博客文章的啓發,我嘗試添加

remove-variable reader, schemaSet 

我也試着拿起$schema來回米Add()和做

[void] $schemaSet.RemoveRecursive($schema) 

這似乎有一定的效果,但仍然有泄漏。我假設XmlSchemaSet的較舊實例仍在使用內存而未被垃圾收集。

問題:我該如何正確教導垃圾回收器它可以回收上面代碼中使用的所有內存?或者更一般地說:我怎樣才能用有限的內存來實現我的目標?

回答

9

Microsoft已經確認這是在PowerShell 2.0中的一個bug,並且他們的狀態,這已在PowerShell中3.0得到解決。

問題是使用Register-ObjectEvent註冊的事件處理程序不是垃圾收集。在向效應初探支持通話,微軟表示,

「我們正在處理在PowerShell中第2節中的錯誤。這個問題實際上是由一個事實,即.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 

然而,這種解決方法是不正確功能。在執行這兩條附加聲明時,仍然處於「正在運行」的任何事件都將丟失。就我而言,這意味着我錯過了驗證錯誤,所以我的腳本輸出不完整。

4

remove-variable,你可以嘗試強制GC收集:

[GC]::Collect() 
+0

這確實使得增加更少,所以這可能有助於實踐;但使用的內存量仍在逐漸增加。 –

+1

在關閉它後添加'$ reader.Dispose()'有助於更多? –

+0

正如[另一個StackOverflow答案](http://stackoverflow.com/a/745965/223837)中所述,這在PowerShell中不是直接可能的,並且它不是必需的,因爲Close()意味着Dispose()按照推薦的Microsoft慣例。 –