Утечка памяти с помощью удаленных вызовов Powershell в C#
у меня есть служба windows, которая делает много удаленных вызовов exchange, чтобы получить некоторую информацию о сервере. Я заметил, что пока проходит время, память, используемая службой, начинает расти, пока не будет создано исключение памяти. Я искал, и похоже, что есть известная утечка памяти в System.Management.Automation
это не утилизирует всю память Runspace
создано при вызове метода close и / или dispose. Я рассмотрел сообщение, которое предлагает использовать CreateOutOfProcessRunspace
на RunspaceFactory
но не знаю, как им пользоваться.
вот как можно воспроизвести проблему: (System.Management.Automation
dll ссылка)
for (int i = 0; i < 1000; i++)
{
var runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
runspace.Close();
runspace.Dispose();
}
если вы запустите этот код, вы увидите, как память увеличивается. Из-за требований, поддержание соединения открытым как можно больше не является хорошим решением.
вы знаете, как я могу исправить эту проблему, даже используя CreateOutOfProcessRunspace
метод RunspaceFactory
или Как правильно распоряжаться памятью?
спасибо заранее
редактировать
я использовал V3 и изменил создание runspace, чтобы использовать метод CreateRunspacePool, и похоже, что утечка исчезла. Большое спасибо за помощь!
2 ответов
Я вижу проблему в PS v3.0, но не в PS v2.0. Вот код, который я использую для см. это (все примеры находятся в PowerShell):
for() {
$runspace = [runspacefactory]::CreateRunspace()
$runspace.Open()
$runspace.Close()
$p = Get-Process -Id $PID
'{0} {1}' -f $p.Handles, ($p.PrivateMemorySize / 1mb)
}
похоже, что ручки и память протекают в v3.0 в приведенном выше коде.
насколько П2.0 этой проблемы нет, возможным решением может быть
запустите службу с помощью PS v2.0, то есть PowerShell.exe -Version 2.0
.
если это невозможно, я могу придумать еще два обходных пути. Одного из них нет чтобы создать runspaces напрямую но используйте . Например, это код не показывает утечку в v3.0:
for() {
$ps = [powershell]::Create()
$p = $ps.AddCommand('Get-Process').AddParameter('Id', $PID).Invoke()
'{0} {1}' -f $p.Handles, ($p.PrivateMemorySize / 1mb)
$ps.Dispose()
}
другой обходной путь, если он применим, может быть использование
[runspacefactory]::CreateRunspacePool()
. Этот путь также не показывает
утечка:
$rs = [runspacefactory]::CreateRunspacePool()
$rs.Open()
for() {
$ps = [powershell]::Create()
$ps.RunspacePool = $rs
$p = $ps.AddCommand('Get-Process').AddParameter('Id', $PID).Invoke()
'{0} {1}' -f $p.Handles, ($p.PrivateMemorySize / 1mb)
$ps.Dispose()
}
#$rs.Close() # just a reminder, it's not called here due to the infinite loop
последний также работает намного быстрее, потому что runspace используется повторно.
Я также столкнулся с той же проблемой, когда я использовал v1 системы.Управление.Автоматизация. Но вопрос был решен с v3 системы.Управление.Автоматизация и изменение кода для использования CreateOutOfProcessRunspace метод
здесь код
using (PowerShellProcessInstance instance = new PowerShellProcessInstance(new Version(4, 0), null, null, false))
{
using (var runspace = RunspaceFactory.CreateOutOfProcessRunspace(new TypeTable(new string[0]), instance))
{
runspace.Open();
using (PowerShell powerShellInstance = PowerShell.Create())
{
powerShellInstance.Runspace = runspace;
var filePath = GetScriptFullName(powerShellScriptType);
powerShellInstance.Commands.AddScript(File.ReadAllText(filePath));
var includeScript = GetIncludeScript();
powerShellInstance.AddParameters(new List<string>
{
userName,
plainPassword,
includeScript
});
Collection<PSObject> psOutput = powerShellInstance.Invoke();
// check the other output streams (for example, the error stream)
if (powerShellInstance.Streams.Error.Count > 0)
{
// error records were written to the error stream.
// do something with the items found.
var exceptions = "";
foreach (var error in powerShellInstance.Streams.Error)
{
exceptions += error.Exception + "\n";
}
throw new InvalidPowerShellStateException(exceptions);
}
return psOutput;
}
}
}