我有一个很长的perl脚本来缓存文件中的一些信息
在哈希中,每隔一段时间(这里,每100000个位置),
它打印该窗口的哈希值,然后尝试
删除散列中的大部分内容,小缓冲区除外
用于下一次迭代.
在哈希中,每隔一段时间(这里,每100000个位置),
它打印该窗口的哈希值,然后尝试
删除散列中的大部分内容,小缓冲区除外
用于下一次迭代.
我说它试图删除内容,因为我的脚本爆炸了
在内存使用中,直到它使用所有内存和崩溃.即使它
似乎delete语句正在减少哈希中的键数
(见下面的打印STDERR)只有少数元素,
脚本的内存消耗猛增,就好像它没有删除一样
内容.如果我注释掉删除语句,它使用相同的
记忆的数量,唯一的区别是它需要更长的时间
重复.删除后似乎减少了键的数量
命令,但不是值的数量.
我确保阅读和没有奇怪的缓冲
输出结果.事实上,如果脚本没有内存不足
我只是注释掉使用%hash的地方,所以我缩小了它
到填写和删除%hash中的条目.
我也尝试使用hashref而不是%hash,但仍然如此
发生.
怎么会在记忆中爆炸?我错过了什么明显的东西
这里?
my %hash; # while ( Read from input ) { # Fill hash here and there with: $hash{$this_p}{$this_c}++ # ... # Then every 100000 entries if ( not $pos % 100000 ) { print STDERR "pre ",scalar %hash,"\n"; warn total_size(\%hash); for my $p ( sort { $a <=> $b } keys %hash ) { last if ( $p > $max_possible{$subset} ); if ( $p + $buffer < $pos ) { print $out "$p\t"; for my $c ( keys %{ $hash{$p} } ) { print $out "$c ". $hash{$p}{$c} . ";"; } print $out "\n"; delete $hash{$p}; } } print STDERR "post ","\n"; warn total_size(\%hash); } #}
输出是这样的:
pre 322484/524288 134297952 at /home/ post 681/524288 4368924 at /home/av pre 681/524288 4368924 at /home/av post 681/524288 4368924 at /home/av pre 681/524288 4368924 at /home/av post 681/524288 4368924 at /home/av pre 629257/1048576 260016542 at /home/ post 344/1048576 8477509 at /home/av pre 1903885/4194304 689633878 at /home/ post 900/4194304 33790436 at /home/a [...]
这是在64位Linux机器上使用perl v5.14.2.
解决方法
在程序运行时,每次传递中散列的元素数量都在增加. 0键(%hash)会告诉你确切的数字,但下面的分子会相似(但更低)
322484 added pre 322484/524288 321803 cleared (99.8% of added) post 681/524288 0 added pre 681/524288 0 cleared (100.0% of added) post 681/524288 0 added pre 681/524288 0 cleared (100.0% of added) post 681/524288 628576 added pre 629257/1048576 628913 cleared (100.0% of added) post 344/1048576 1903541 added pre 1903885/4194304 1902641 cleared (100.0% of added) post 900/4194304
分母只是在增长,因为分子正在增长.这不相关.这不是累积增长.即使你每次都有一个新的哈希,它也会变得那么大.
分子只会增长,因为您添加到哈希的元素数量会增加.如您所见,清算代码非常有效.
这根本不像是内存泄漏;看起来你实际上正在使用内存.也许你应该更频繁地清除它?
代替
if (not $pos % 100000) { ... }
使用
if (keys(%hash) >= 1_000_000) { ... }
或者如果你想要定期反馈,
if (++$since_last >= 100_000 || keys(%hash) >= 1_000_000) { $since_last = 0; ... }
根据需要调整限制.