2012-06-27 12 views
11

Tôi đã tạo tập lệnh PowerShell lặp lại trên một số lượng lớn tệp XML Schema (.xsd) và mỗi đối tượng tạo một đối tượng .NET XmlSchemaSet, gọi Add()Compile() để thêm lược đồ vào đó và in ra tất cả các lỗi xác thực.Làm thế nào để hướng dẫn PowerShell thu thập rác các đối tượng .NET như XmlSchemaSet?

Tập lệnh này hoạt động chính xác, nhưng có rò rỉ bộ nhớ ở đâu đó, khiến cho nó tiêu tốn gigabyte bộ nhớ nếu chạy trên 100 tệp.

Những gì tôi chủ yếu làm trong một vòng lặp như sau:

$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() 

(Một kịch bản đầy đủ để tái tạo vấn đề này có thể được tìm thấy trong ý chính sau:. https://gist.github.com/3002649 Chỉ cần chạy nó, và xem sự gia tăng sử dụng bộ nhớ trong Task Manager hoặc Process Explorer.)

Lấy cảm hứng từ một số bài đăng trên blog, tôi đã cố gắng thêm

remove-variable reader, schemaSet 

tôi cũng đã cố gắng chọn lên các $schema fro m Add() và đang thực hiện

[void] $schemaSet.RemoveRecursive($schema) 

Điều này dường như có tác dụng nhưng vẫn bị rò rỉ. Tôi cho rằng các phiên bản cũ hơn của XmlSchemaSet vẫn đang sử dụng bộ nhớ mà không bị thu gom rác.

Câu hỏi: Làm cách nào để dạy cho người thu gom rác đúng cách mà nó có thể đòi lại tất cả bộ nhớ được sử dụng trong mã ở trên? Hay nói chung hơn: làm thế nào tôi có thể đạt được mục tiêu của mình với một lượng bộ nhớ giới hạn?

Trả lời

9

Microsoft đã xác nhận rằng đây là một lỗi trong PowerShell 2.0, và họ tuyên bố rằng điều này đã được giải quyết trong PowerShell 3.0.

Vấn đề là một trình xử lý sự kiện được đăng ký sử dụng Register-ObjectEvent không phải là rác được thu thập. Trong phản ứng với một cuộc gọi hỗ trợ, Microsoft nói rằng

"chúng tôi đang làm việc với một lỗi trong PowerShell v.2. Vấn đề này là do thực sự bởi thực tế là các trường hợp đối tượng .NET không còn phát hành do các trình xử lý sự kiện không được tự giải phóng. Vấn đề không còn có thể lặp lại với PowerShell v.3 ".

Giải pháp tốt nhất, theo như tôi thấy, là giao diện giữa PowerShell và .NET ở một cấp độ khác: thực hiện xác thực hoàn toàn bằng mã C# (được nhúng trong tập lệnh PowerShell) và chỉ trả lại một danh sách các đối tượng ValidationEventArgs. Xem kịch bản sinh sản cố định tại https://gist.github.com/3697081: tập lệnh đó có chức năng chính xác và không bị rò rỉ bộ nhớ.

(Cảm ơn Bộ phận hỗ trợ của Microsoft đã giúp tôi tìm giải pháp này.)


Ban đầu Microsoft cung cấp một workaround, mà là sử dụng $xyzzy = Register-ObjectEvent -SourceIdentifier XYZZY, và sau đó ở cuối làm như sau:

Unregister-Event XYZZY 
Remove-Job $xyzzy -Force 

Tuy nhiên, workaround này là chức năng không chính xác. Bất kỳ sự kiện nào vẫn còn 'đang bay' bị mất tại thời điểm hai câu lệnh bổ sung này được thực hiện. Trong trường hợp của tôi, điều đó có nghĩa là tôi bỏ sót các lỗi xác nhận, vì vậy đầu ra của tập lệnh của tôi chưa hoàn thành.

4

Sau remove-variable bạn có thể cố gắng ép buộc thu GC:

[GC]::Collect() 
+0

Điều đó làm cho mức tăng ít hơn, vì vậy điều này có thể hữu ích trong thực tế; nhưng lượng bộ nhớ được sử dụng vẫn tăng dần. –

+1

thêm '$ reader.Dispose()' sau khi đóng nó giúp ích nhiều hơn? –

+0

Như được mô tả trong [một câu trả lời StackOverflow] (http://stackoverflow.com/a/745965/223837) điều này là không thể trực tiếp trong PowerShell, và nó không cần thiết vì 'Close()' ngụ ý một 'Dispose()' theo quy ước Microsoft được đề xuất. –