Как указывает @BACON, основная проблема здесь вызвана циклом (вероятно) нескольких тысяч замен.
Каждый раз, когда выполняется строка замены:
$txtsourceFile = $txtsourceFile -replace $findReplaceitem.$OldIssueKey , $findReplaceitem.$IssueKey
Сначала PowerShell имееткусок памяти для $txtsourceFile
. Он выделяет новый кусок памяти для хранения копии данных после замены текста.
Обычно это нормально, так как у вас будет один действительный кусок памяти с текстом замены и «недействительным»скопировать с оригинальным текстом. Поскольку у большинства людей (относительно) много памяти, и мы обычно можем справиться с этой «утечкой» в .NET, периодически запуская сборщик мусора в фоновом режиме, чтобы «очистить» эти недопустимые данные.
Проблема, с которой мы столкнулисьдело в том, что когда мы зацикливаемся несколько тысяч раз быстро, мы также быстро генерируем несколько тысяч копий данных. В конечном итоге у вас заканчивается свободная память, прежде чем сборщик мусора сможет запустить и очистить тысячи недействительных копий данных (например, 3,2 ГБ). См .: Нет сборки мусора во время выполнения конвейера PowerShell
Существует несколько способов обойти это:
Решение 1. Большой и медленный методи Неэффективный способ
Если вам нужно работать с целым файлом (т. е. с новой строкой), вы можете использовать один и тот же код и периодически запускать сборщик мусора во время выполнения, чтобы лучше управлять памятью:
$count = 0
ForEach ($findReplaceItem in $findReplaceList) {
$txtsourceFile = $txtsourceFile -replace $findReplaceitem.$OldIssueKey, $findReplaceitem.$IssueKey
if(($count % 200) -eq 0)
{
[System.GC]::GetTotalMemory('forceFullCollection') | out-null
}
$count++
}
Это делает 2 вещи:
- Запуск сборщика мусора каждые 200 циклов (
$count
модуль 200). - Остановка текущего выполнения и принудительное выполнениеколлекция.
Примечание:
Обычно вы используете:
[GC]::Collect()
Но в соответствии с Обращение к сборке мусора PowerShellошибка в J House Consulting , которая не всегда срабатывает при попытке принудительно собрать коллекцию внутри цикла. Использование:
[System.GC]::GetTotalMemory('forceFullCollection')
Полностью останавливает выполнение до тех пор, пока сборка мусора не будет завершена перед возобновлением.
Решение 2. Более быстрый и более эффективный метод памяти, по одной строке за раз
Если вы можете выполнять все замены по одной строке за раз, то вы можете использовать [System.IO.StreamReader]
для потоковой передачи файла и обработки по одной строке за раз и [System.IO.StreamWriter]
, чтобы написать это.
try
{
$SR = New-Object -TypeName System.IO.StreamReader -ArgumentList $sourceFile.FullName
$SW = [System.IO.StreamWriter] $outputFullFileName
while ($line = $SR.ReadLine()) {
#Loop through Replacements
ForEach ($findReplaceItem in $findReplaceList) {
$Output = $line -replace $findReplaceitem.$OldIssueKey, $findReplaceitem.$IssueKey
}
$SW.WriteLine($output)
}
$SR.Close() | Out-Null
$SW.Close() | Out-Null
}
finally
{
#Cleanup
if ($SR -ne $null)
{
$SR.dispose()
}
if ($SW -ne $null)
{
$SW.dispose()
}
}
Это должно работать на порядок быстрее, потому что вы будете работать со строкой за раз и не будете создавать тысячи копий всего файлас каждой заменой.