PowerShell 管道执行时没有垃圾回收
No garbage collection while PowerShell pipeline is executing
更新:以下错误似乎已通过 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
这将导致 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
...问题依旧。内存使用再次不受控制地增长。我尝试了几种变体,例如添加 [GC]::WaitForPendingFinalizers()
、Start-Sleep -Seconds 10
等。我尝试更改垃圾收集器 latency modes and forcing PowerShell to use server garbage collection 无济于事。我只是无法让垃圾收集器在管道执行时执行它的操作。
这在 PowerShell 2.0 中根本不是问题。有趣的是 $null = Get-DummyData
似乎也可以在没有内存问题的情况下工作。所以它似乎与管道有关,而不是我们正在生成大量字符串这一事实。
如何防止我的内存在长管道期间不受控制地增长?
旁注:
我的 Get-DummyData 函数仅用于演示目的。我的现实问题是我无法使用 Get-Content
或 Import-Csv
在 PowerShell 中读取大文件。不,我不是将这些文件的内容存储在变量中。我 strictly using the pipeline 就像我应该的那样。 Get-Content .\super-huge-file.txt | Out-Null
产生同样的问题。
当您执行一些不寻常的事情(如处理大量文本文件)时,发现本机 cmdlet 不能完美满足这一点并不少见。就个人而言,我发现使用 System.IO.StreamReader:
编写脚本时,在 Powershell 中处理大文件会好得多
$SR = New-Object -TypeName System.IO.StreamReader -ArgumentList 'C:\super-huge-file.txt';
while ($line = $SR.ReadLine()) {
Do-Stuff $line;
}
$SR.Close() | Out-Null;
注意在ArgumentList中应该使用绝对路径。对我来说,它似乎总是假设您位于具有相对路径的主目录中。
Get-Content
简单来说就是将整个对象作为数组读入内存,然后输出。我认为它只是调用 System.IO.File.ReadAllLines()。
我不知道有什么方法可以告诉 Powershell 在完成后立即从管道中丢弃项目,或者函数可以异步 return 项目,因此它会保留顺序。它可能不允许这样做,因为它没有自然的方式来告诉以后不会使用该对象,或者以后的对象不需要引用早期的对象。
Powershell 的另一个好处是您可以经常 adopt the C# answers, too. I've never tried File.ReadLines,但看起来它也可能很容易使用。
这里有几点需要指出。首先,GC 调用确实在管道中起作用。这是一个只调用 GC 的管道脚本:
1..10 | Foreach {[System.GC]::Collect()}
这是脚本执行期间 GC 的性能图 运行:
但是,仅仅因为您调用了 GC,并不意味着专用内存使用量将 return 达到脚本启动前的值。 GC 收集只会收集不再使用的内存。如果存在对对象的根引用,则它不符合收集(释放)的条件。因此,虽然 GC 系统通常不会在 C/C++ 意义上发生泄漏,但它们可能拥有内存储备,这些内存在对象上的保存时间可能比它们应该保存的时间更长。
在使用内存分析器查看时,似乎大部分多余内存都被带有参数绑定信息的字符串副本占用:
这些字符串的根如下所示:
我想知道是否有某些日志记录功能导致 PowerShell 挂在字符串化表单管道绑定对象上?
顺便说一句,在这种特定情况下,分配给 $null 以忽略输出的内存效率更高:
$null = GetDummyData
此外,如果您需要简单地编辑文件,请查看 PowerShell Community Extensions 3.2.0 中的 Edit-File
命令。只要您不使用 SingleString 开关参数,它就应该是内存高效的。
更新:以下错误似乎已通过 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
这将导致 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
...问题依旧。内存使用再次不受控制地增长。我尝试了几种变体,例如添加 [GC]::WaitForPendingFinalizers()
、Start-Sleep -Seconds 10
等。我尝试更改垃圾收集器 latency modes and forcing PowerShell to use server garbage collection 无济于事。我只是无法让垃圾收集器在管道执行时执行它的操作。
这在 PowerShell 2.0 中根本不是问题。有趣的是 $null = Get-DummyData
似乎也可以在没有内存问题的情况下工作。所以它似乎与管道有关,而不是我们正在生成大量字符串这一事实。
如何防止我的内存在长管道期间不受控制地增长?
旁注:
我的 Get-DummyData 函数仅用于演示目的。我的现实问题是我无法使用 Get-Content
或 Import-Csv
在 PowerShell 中读取大文件。不,我不是将这些文件的内容存储在变量中。我 strictly using the pipeline 就像我应该的那样。 Get-Content .\super-huge-file.txt | Out-Null
产生同样的问题。
当您执行一些不寻常的事情(如处理大量文本文件)时,发现本机 cmdlet 不能完美满足这一点并不少见。就个人而言,我发现使用 System.IO.StreamReader:
编写脚本时,在 Powershell 中处理大文件会好得多$SR = New-Object -TypeName System.IO.StreamReader -ArgumentList 'C:\super-huge-file.txt';
while ($line = $SR.ReadLine()) {
Do-Stuff $line;
}
$SR.Close() | Out-Null;
注意在ArgumentList中应该使用绝对路径。对我来说,它似乎总是假设您位于具有相对路径的主目录中。
Get-Content
简单来说就是将整个对象作为数组读入内存,然后输出。我认为它只是调用 System.IO.File.ReadAllLines()。
我不知道有什么方法可以告诉 Powershell 在完成后立即从管道中丢弃项目,或者函数可以异步 return 项目,因此它会保留顺序。它可能不允许这样做,因为它没有自然的方式来告诉以后不会使用该对象,或者以后的对象不需要引用早期的对象。
Powershell 的另一个好处是您可以经常 adopt the C# answers, too. I've never tried File.ReadLines,但看起来它也可能很容易使用。
这里有几点需要指出。首先,GC 调用确实在管道中起作用。这是一个只调用 GC 的管道脚本:
1..10 | Foreach {[System.GC]::Collect()}
这是脚本执行期间 GC 的性能图 运行:
但是,仅仅因为您调用了 GC,并不意味着专用内存使用量将 return 达到脚本启动前的值。 GC 收集只会收集不再使用的内存。如果存在对对象的根引用,则它不符合收集(释放)的条件。因此,虽然 GC 系统通常不会在 C/C++ 意义上发生泄漏,但它们可能拥有内存储备,这些内存在对象上的保存时间可能比它们应该保存的时间更长。
在使用内存分析器查看时,似乎大部分多余内存都被带有参数绑定信息的字符串副本占用:
这些字符串的根如下所示:
我想知道是否有某些日志记录功能导致 PowerShell 挂在字符串化表单管道绑定对象上?
顺便说一句,在这种特定情况下,分配给 $null 以忽略输出的内存效率更高:
$null = GetDummyData
此外,如果您需要简单地编辑文件,请查看 PowerShell Community Extensions 3.2.0 中的 Edit-File
命令。只要您不使用 SingleString 开关参数,它就应该是内存高效的。