PowerShell内存泄漏误解

时间:2015-09-21 20:13:07

标签: powershell memory-management memory-leaks concatenation

PowerShell的新手,所以通过实践来学习。

我创建的过程有效,但最终锁定我的机器直到它完成,耗尽了所有内存。我认为通过强制垃圾收集器,以及从for-each语句转移到使用%()来遍历所有内容,我已经解决了这个问题。

流程快速概要:需要将多个SharePoint日志文件合并为单个日志文件,以跟踪所有公司的使用情况。不同的SharePoint站点。 PowerShell遍历SP服务器上的所有日志目录,并检查目录中的每个文件(如果它已存在于我的本地计算机上)。如果确实存在,则附加文件文本,否则直接复制。对SharePoint Log Server上的每个文件和目录进行冲洗重复。在每个循环之间,我强迫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千兆内存的7.5倍。)

提前致谢。

2 个答案:

答案 0 :(得分:3)

不要那样嵌套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。 / p>

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

为了解释我的答案,如果你在管道中使用ForEach-Object,它会将所有内容保存在内存中(无论你的GC调用如何)。使用ForEach循环不会这样做,并应该处理您的问题。

答案 1 :(得分:2)

您可能会发现https://msdn.microsoft.com/de-de/library/system.drawing.font%28v=vs.110%29.aspxthis有帮助。

简而言之:当您需要处理大量数据或I / O操作时,Add-Content,Get-Content和Out-File非常方便,但却非常慢。您希望回退到thisStreamReader .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()