我有PowerShell script读取大型CSV文件(4GB +),查找某些行,然后将这些行写入其他文件。
我注意到,当它到达" echo"在$ datafile文件中处理$ datacounter总行时#34;"脚本的最后一行,直到5-10分钟后才真正完成。
那段时间做了什么?完成后,内存使用率会大幅下降。有没有办法强制它在脚本结束时清除内存?
Screenshot of script timestamps
以下是我的脚本的最终版本供参考。
# Get the filename
$datafile = Read-Host "Filename"
$dayofweek = Read-Host "Day of week (IE 1 = Monday, 2 = Tuesday..)"
$campaignWriters = @{}
# Create campaign ID hash table
$campaignByID = @{}
foreach($c in (Import-Csv 'campaigns.txt' -Delimiter '|')) {
foreach($id in ($c.CampaignID -split ' ')) {
$campaignByID[$id] = $c.CampaignName
}
foreach($cname in ($c.CampaignName)) {
$writer = $campaignWriters[$cname] = New-Object IO.StreamWriter($dayofweek + $cname + '_filtered.txt')
if($dayofweek -eq 1) {
$writer.WriteLine("ID1|ID2|ID3|ID4|ID5|ID6|Time|Time-UTC-Sec")
}
}
}
# Display the campaigns
$campaignByID.GetEnumerator() | Sort-Object Value
# Read in data file
$encoding = [Text.Encoding]::GetEncoding('iso-8859-1')
$datareader = New-Object IO.StreamReader($datafile, $encoding)
$datacounter = 0
echo "Starting.."
get-date -Format g
while (!$datareader.EndOfStream) {
$data = $datareader.ReadLine().Split('þ')
# Find the Campaign in the hashtable
$campaignName = $campaignByID[$data[3]]
if($campaignName) {
$writer = $campaignWriters[$campaignName]
# If a campaign name was returned from the hash, add the line using that campaign's writer
$writer.WriteLine(($data[20,3,5,8,12,14,0,19] -join '|'))
}
$datacounter++;
}
$datareader.Close()
foreach ($writer in $campaignWriters.Values) {
$writer.Close()
}
echo "Done!"
get-date -Format g
echo "Processed $datacounter total lines in the $datafile file"
答案 0 :(得分:0)
它可能会也可能不会起作用,但您可以尝试告诉垃圾收集运行:
std::
你没有对它进行细粒度的控制,在运行它之前,它可能有助于[System.GC]::Collect()
或将变量设置为Remove-Variable
以便不存在对数据的引用了。
答案 1 :(得分:0)
我假设campaigns.txt
是您所指的多GB文件。如果它是其他文件,这可能没有多大意义。
如果是这样,调用内部括号import-csv
然后使用foreach
语句迭代它们是什么驱使你的内存使用率如此之高。更好的选择是使用PowerShell管道从文件中流式传输记录,而无需同时将所有记录保存在内存中。您可以通过将foreach
语句更改为ForEach-Object
cmdlet:
Import-Csv 'campaigns.txt' -Delimiter '|' | ForEach-Object {
foreach($id in ($_.CampaignID -split ' ')) {
$campaignByID[$id] = $_.CampaignName
}
}
.NET垃圾收集器是优化的情况,其中大多数对象都是短暂的。因此,这种变化应该会导致显着的性能提升,并最终减少停机时间。
我建议反对强制使用[System.GC]::Collect()
进行垃圾回收,垃圾回收器最好知道它应该运行的时间。原因很复杂,如果你真的想知道为什么会这样,Maoni's blog有很多关于.NET环境中垃圾收集的细节。