Утечка памяти при использовании Powershell Remote Calls в C#
У меня есть служба Windows, которая делает много удаленных вызовов обмена, чтобы получить некоторую информацию о сервере. Я заметил, что с течением времени объем памяти, используемой службой, начинает расти, пока не будет выброшено исключение памяти. Я искал, и похоже, что есть известная утечка памяти в System.Management.Automation
который не располагает всей памятью о Runspace
создается при вызове метода close и / или dispose. Я просмотрел пост, в котором предлагается использовать CreateOutOfProcessRunspace
из RunspaceFactory
но не уверен, как его использовать.
Вот как можно воспроизвести проблему: (System.Management.Automation
длл ссылается)
for (int i = 0; i < 1000; i++)
{
var runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
runspace.Close();
runspace.Dispose();
}
Если вы запустите этот код, вы увидите, как увеличивается память. Из-за требований держать как можно более открытым соединение не является хорошим решением.
Знаете ли вы, как я могу решить эту проблему, даже используя CreateOutOfProcessRunspace
метод RunspaceFactory
или как правильно распорядиться памятью?
заранее спасибо
РЕДАКТИРОВАТЬ
Я использовал V3 и изменил создание пространства выполнения, чтобы использовать метод 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 в коде выше.
Поскольку v2.0 не имеет этой проблемы, одним из возможных решений может быть запуск службы с использованием PS v2.0, т.е. PowerShell.exe -Version 2.0
,
Если это невозможно, я могу придумать еще два обходных пути. Один из них - не создавать пространства выполнения напрямую, а использовать [powershell]
вместо. Например, этот код не показывает утечку в 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
Последний также работает намного быстрее, потому что пространство выполнения отчасти используется повторно.
Я также столкнулся с той же проблемой, когда я использовал https://www.nuget.org/package s/System.Management.Automation/. Но проблема была решена с помощью v3 System.Management.Automation и изменения кода для использования метода 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;
}
}
}