10

我有一个 Windows 服务,它正在执行大量交换远程调用以获取一些服务器信息。我注意到,只要时间过去,服务使用的内存就会开始增长,直到引发内存异常。我已经搜索过,看起来有一个已知的内存泄漏,在调用 close 和/或 dispose 方法时System.Management.Automation不会释放创建的所有内存。Runspace我查看了一篇建议使用CreateOutOfProcessRunspaceofRunspaceFactory但不确定如何使用它的帖子。

以下是如何重现该问题:(System.Management.Automation引用 dll)

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

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

你知道我如何解决这个问题,即使使用CreateOutOfProcessRunspace方法RunspaceFactory或如何正确处理内存?

提前致谢

编辑

我正在使用 V3 并将运行空间创建更改为使用 CreateRunspacePool 方法,看起来泄漏已经消失了。非常感谢你的帮助!

4

2 回答 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.

如果这是不可能的,我可以想到另外两种解决方法。其中之一不是直接创建运行空间,而是使用[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

最后一个也工作得更快,因为运行空间是一种重用。

于 2014-04-23T04:43:53.870 回答
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;
                }
            }
        }
于 2015-09-14T10:11:41.167 回答