【问题标题】:PowerShell Memory leak misunderstandingPowerShell 内存泄漏误区
【发布时间】:2015-09-21 20:13:07
【问题描述】:

PowerShell 新手,所以在实践中学习。

我创建的进程有效,但它最终会锁定我的机器直到它完成,占用所有内存。我想我已经通过强制垃圾收集器解决了这个问题,并且还从 for-each 语句转移到使用 %() 循环所有内容。

流程快速概要:需要将多个 SharePoint 日志文件合并为单个文件,以跟踪公司所有不同 SharePoint 网站的使用情况。 PowerShell 遍历 SP 服务器上的所有日志目录,并检查目录中的每个文件是否已存在于我的本地计算机上。如果它确实存在,它会附加文件文本,否则它会直接复制。对 SharePoint 日志服务器上的每个文件和目录重复冲洗。在每个循环之间,我强制执行 GC,因为......好吧,因为我的基本理解是循环变量保存在内存中,我想刷新它们。我可能看错了。所以这是有问题的脚本。

$FinFiles = 'F:\Monthly Logging\Logs'

dir -path '\\SP-Log-Server\Log-Directory' | ?{$_.PSISContainer} | %{
    $CurrentDir = $_
    dir $CurrentDir.FullName | ?(-not $_.PSISContainer} | %{
        if($_.Extension -eq ".log"){
            $DestinationFile = $FinFiles + '\' + $_.Name
            if((Test-Path $DestinationFile) -eq $false){
                New-Item -ItemType file -path $DestinationFile -Force
                Copy-Item $_.FullName $DestinationFile
            }
            else{
                $A = Get-Content $_.FullName ; Add-Content $DestinationFile $A
                Write-Host "Log File"$_.FullName"merged."
            }
        [GC]::Collect()
    }
    [GC]::Collect()
}

已完成/附加的日志文件变得非常非常大(最小 300 MB,最大 1GB)。我不是在关闭我应该做的事情,还是在记忆中保持打开的状态? (目前它占我 8 Gig 内存总数的 7.5。)

提前致谢。

【问题讨论】:

  • 您已经有 2 个很好的答案和有用的内容。在这里,我为您提供其他内容,只是对您的代码的评论。虽然真正的解决方案可能允许您删除对 [GC]::Collect() 的任何使用,但我很确定在您提供的代码中,您只需要第一次调用 [GC]::Collect(),在内循环。对 [GC]::Collect() 的外部调用(第二个)仅在第一个之后或没有文件被处理之后才被调用,因此它基本上什么都不做。
  • 感谢您的来电。我有两个的原因是每个循环一个。我不知道它是否会从内存中清除每个循环项目。但是感谢@TheMadTechnician,我能够在没有垃圾收集器的情况下获得最终产品。

标签: powershell memory-management memory-leaks concatenation


【解决方案1】:

不要嵌套Get-ChildItem 这样的命令。请改用通配符。试试:dir "\\SP-Log-Server\Log-Directory\*\*.log"。那应该从一开始就改善事情。然后将其移动到 ForEach($X in $Y){} 循环而不是 ForEach-Object{} 循环(您现在正在使用的)。我打赌这可以解决您的问题。

所以,在我的脑海中重新写了一遍:

$FinFiles = 'F:\Monthly Logging\Logs'

ForEach($LogFile in (dir -path '\\SP-Log-Server\Log-Directory\*\*.log')){
    $DestinationFile = $FinFiles + '\' + $LogFile.Name
        if((Test-Path $DestinationFile) -eq $false){
            New-Item -ItemType file -path $DestinationFile -Force
            Copy-Item $LogFile.FullName $DestinationFile
        }
        else{
            $A = Get-Content $LogFile.FullName ; Add-Content $DestinationFile $A
            Write-Host "Log File"$LogFile.FullName"merged."
        }
    }
}

编辑:哦,对了,Alexander Obersht 可能也很正确。您也可以从 StreamReader 方法中受益。至少你应该使用-readcount 参数到Get-Content,并且没有理由将其保存为变量,只需将其直接通过管道传递给add-content cmdlet。

Get-Content $LogFile.FullName -ReadCount 5000| Add-Content $DestinationFile

为了进一步解释我的答案,如果您在管道中使用ForEach-Object,它会将所有内容保存在内存中(无论您的 GC 调用如何)。使用 ForEach 循环不会执行此操作,应该解决您的问题。

【讨论】:

  • 我会试一试。我以为我一开始就做了ForEach($X in $Y),但我明天回去工作时会仔细检查以前的版本。主要目标是在运行时仍能在我的机器上工作。
  • 这似乎效果最好。我仍然看到我的内存使用量有所增加,但是当它完成一个文件时它确实下降了。所以我仍然可以在这台机器上工作,而追加/连接发生。谢谢!
  • 我可能说得太早了。我做了你在主代码块中放置的内容,它一直有效,直到我到达与上次运行大致相同的位置。我的 8GB 内存的上限约为 7.5GB。所以每个文档运行后内存仍然没有清除。
  • 也许是第三次魅力。我删除了$A 变量并添加了您提到的-ReadCount。这一次似乎是在保持记忆清晰。根本没有使用斜坡。
【解决方案2】:

您可能会发现 thisthis 很有帮助。

简而言之:当您需要处理大量数据或 I/O 操作时,Add-Content、Get-Content 和 Out-File 很方便,但速度却很慢。在像您这样的情况下,您想回退到 StreamReaderStreamWriter .NET 类来优化性能和/或内存使用。

代码示例:

$sInFile = "infile.txt"
$sOutFile = "outfile.txt"

$oStreamReader = New-Object -TypeName System.IO.StreamReader -ArgumentList @($sInFile)
# $true sets append mode.
$oStreamWriter = New-Object -TypeName System.IO.StreamWriter -ArgumentList @($sOutFile, $true)

foreach ($sLine in $oStreamReader.ReadLine()) {
    $oStreamWriter.WriteLine($sLine)
}

$oStreamReader.Close()
$oStreamWriter.Close()

【讨论】:

  • 这是否意味着我必须包含和学习那些 .NET 库?
  • 很容易找到如何在 PS 中使用它们的示例 - 只需点击我帖子中的前两个链接即可。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-02
  • 1970-01-01
相关资源
最近更新 更多