Вопрос по powershell, xsd, memory-leaks, garbage-collection, xmlschemaset – Как настроить PowerShell для сбора мусора .NET-объектов, таких как XmlSchemaSet?

11

Я создал скрипт PowerShell, который перебирает большое количество файлов XML Schema (.xsd), и для каждого из них создается .NETXmlSchemaSet объект, звонкиAdd() а такжеCompile() добавить схему и распечатать все ошибки проверки.

Этот скрипт работает правильно, но где-то происходит утечка памяти, из-за чего он потребляет гигабайты памяти, если выполняется на сотнях файлов.

Что я по сути делаю в цикле, так это следующее:

$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 все еще используют память без сбора мусора.

The question: Как правильно научить сборщик мусора, чтобы он мог вернуть всю память, использованную в коде выше? Или в более общем плане: как мне достичь своей цели с ограниченным объемом памяти?

Ваш Ответ

2   ответа
4

Послеremove-variable Вы можете попытаться форсировать сбор GC:

[GC]::Collect()
Как описано вanother StackOverflow answer это не возможно напрямую в PowerShell, и это не обязательно, потому чтоClose() подразумеваетDispose() согласно рекомендуемой конвенции Microsoft. Marnix Klooster
Нет проблем - не могли бы вы дать краткое описание того, как именно вы используете powershell.exe.config для загрузки .NET 4.0 framework & quot; как ответ на этот вопрос:stackoverflow.com/q/745956/223837? Спасибо! Marnix Klooster
Это делает увеличение меньше, так что это может помочь на практике; но объем используемой памяти все еще постепенно увеличивается. Marnix Klooster
@MarnixKlooster Я использую powershell.exe.config для загрузки .NET 4.0 Framework, и я могу сделать $ reader.dispose (). Извините за плохую информацию.
добавив$reader.Dispose() после закрытия это поможет больше?
9

что это ошибка в PowerShell 2.0, и заявляет, что она была устранена в PowerShell 3.0.

Проблема в том, что обработчик событий, зарегистрированный с помощью Register-ObjectEvent, не является сборщиком мусора. В ответ на звонок в службу поддержки Microsoft сказала, что

"we’re dealing with a bug in PowerShell v.2. The issue is caused actually by the fact that the .NET object instances are no longer released due to the event handlers not being released themselves. The issue is no longer reproducible with 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

Howeverэтот обходной путь является функционально неправильным. Любые события, которые все еще «в полете»; теряются в момент выполнения этих двух дополнительных операторов. В моем случае это означает, что я пропускаю ошибки проверки, поэтому вывод моего скрипта неполон.

Похожие вопросы