PowerShell管道执行时没有垃圾回收

Phi*_*hil 10 memory powershell garbage-collection powershell-3.0

更新: PowerShell 5似乎解决了以下错误.该错误仍然存​​在于3和4中.因此,除非您运行PowerShell 2或5,否则不要使用管道处理任何大型文件.


请考虑以下代码段:

function Get-DummyData() {
    for ($i = 0; $i -lt 10000000; $i++) {
        "This is freaking huge!! I'm a ninja! More words, yay!"
    }
}

Get-DummyData | Out-Null
Run Code Online (Sandbox Code Playgroud)

这将导致PowerShell内存使用量无法控制地增长.执行Get-DummyData | Out-Null几次后,我看到PowerShell内存使用量一直达到4 GB.

根据ANTS Memory Profiler,我们在垃圾收集器的终结队列中有很多东西.当我打电话时[GC]::Collect(),内存从4 GB变为仅70 MB.因此,严格来说,我们没有内存泄漏.

现在,[GC]::Collect()当我完成一个长期的管道操作时,能够打电话对我来说还不够好.我需要管道操作期间进行垃圾收集.但是,如果我尝试[GC]::Collect()在管道执行时调用...

function Get-DummyData() {
    for ($i = 0; $i -lt 10000000; $i++) {
        "This is freaking huge!! I'm a ninja! More words, yay!"

        if ($i % 1000000 -eq 0) {
            Write-Host "Prompting a garbage collection..."
            [GC]::Collect()
        }
    }
}

Get-DummyData | Out-Null
Run Code Online (Sandbox Code Playgroud)

......问题依然存在 内存使用率再次无法控制地增长.我已经试过这几个变化,如添加[GC]::WaitForPendingFinalizers(),Start-Sleep -Seconds 10等我试图改变垃圾收集器的延迟模式,迫使PowerShell来使用服务器垃圾收集无济于事.在管道执行时,我无法让垃圾收集器完成它的工作.

这在PowerShell 2.0中根本不是问题.值得注意的是,$null = Get-DummyData似乎也没有内存问题.所以它似乎与管道有关,而不是我们生成大量字符串的事实.

如何防止我的记忆在长管道中无法控制地增长?

边注:

我的Get-DummyData函数仅用于演示目的.我的现实问题是我无法使用Get-Content或读取PowerShell中的大文件Import-Csv.不,我没有将这些文件的内容存储在变量中.我严格按照我应该使用的管道.Get-Content .\super-huge-file.txt | Out-Null产生同样的问题.

Kei*_*ill 7

这里有几点需要指出.首先,GC调用在管道中工作.这是一个只调用GC的管道脚本:

1..10 | Foreach {[System.GC]::Collect()}
Run Code Online (Sandbox Code Playgroud)

这是脚本运行期间GC的perfmon图:

在此输入图像描述

但是,仅仅因为您调用GC并不意味着私有内存使用将返回到脚本启动之前的值.GC收集器仅收集不再使用的内存.如果存在对对象的根对引用,则不符合收集(释放)的条件.因此,尽管GC系统通常不会在C/C++意义上泄漏,但它们可能具有可能比应有的更长时间保留对象的内存保持.

在使用内存分析器查看此内容时,似乎大量的多余内存被带有参数绑定信息的字符串副本占用:

在此输入图像描述

这些字符串的根看起来像这样:

在此输入图像描述

我想知道是否有一些日志记录功能导致PowerShell挂起到字符串形式的管道绑定对象?

BTW在这种特定情况下,分配给$ null忽略输出的内存效率要高得多:

$null = GetDummyData
Run Code Online (Sandbox Code Playgroud)

此外,如果您只需编辑文件,请查看PowerShell Community Extensions 3.2.0中的Edit-File命令.只要不使用SingleString开关参数,它应该是内存有效的.