使用C#中的Powershell远程调用存在内存泄漏问题

10

我有一个 Windows 服务,需要进行大量的 Exchange 远程调用以获取一些服务器信息。我注意到随着时间的推移,服务使用的内存逐渐增加,直到抛出内存异常。我搜索了一下,发现 System.Management.Automation 中存在已知的内存泄漏问题,该问题在调用关闭和/或处理方法时不会处理所有 Runspace 的内存。我查看了一篇文章,建议使用 RunspaceFactoryCreateOutOfProcessRunspace 方法,但不确定如何使用。

以下是如何重现此问题的步骤:(引用了 System.Management.Automation dll)

for (int i = 0; i < 1000; i++)
{
    var runspace = RunspaceFactory.CreateRunspace();
    runspace.Open();
    runspace.Close();
    runspace.Dispose();
}

如果您运行此代码,则会看到内存如何增加。由于要求,尽可能保持连接打开并不是一个好的解决方案。

您知道我如何解决此问题,即使使用RunspaceFactoryCreateOutOfProcessRunspace方法或如何正确释放内存吗?

提前致谢

编辑

我正在使用V3,并将runspace创建更改为使用CreateRunspacePool方法,看起来泄漏问题已经解决。非常感谢您的帮助!


是的,看起来你遇到了一个问题。也许有一种解决方法。你用这个运行空间做什么? - Roman Kuzmin
@Manuel,为什么标题说是“远程”调用有问题?你的代码片段只创建了一个Runspace,实际上并没有执行任何PS调用,无论是远程还是本地。 - Igor Brejc
2个回答

4

我可以在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

如果不可能,我可以想到另外两个解决方法。其中一个是不直接创建runspace,而是使用[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

最后一个例子也更快,因为运行空间被重复使用。

1
我在PowerShell v4 on .NET 4.5.1上没有看到任何问题。第一个runspace打开后,在GC之后,私有字节推送到13,892K,经过1000次迭代(和最终的GC)后,私有字节为14,076K。增加不多,只有约184K。句柄计数和线程计数也保持稳定。 - Keith Hill
谢谢您的回答!我会尝试这些解决方法并告诉您结果。 - Manuel Quintero
我之前在使用V3版本,将runspace的创建方式更改为使用CreateRunspacePool方法,现在看起来内存泄漏问题已经解决了。非常感谢 :) - Manuel Quintero

3
当我使用System.Management.Automation的v1版本时,我也遇到了同样的问题。但是,通过使用System.Management.Automation的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;
                }
            }
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接