嗨,我在Perl变量中处理大字符串数据(它的原始邮件正文,所以它可以包含附件)。Perl的substr有个有趣的问题。似乎是它的泄漏或我做错了什么(如果是,是什么?)。考虑代码:
#!/usr/local/bin/perl
use strict;
my $str = 'a'x10_000_000;
system("ps up $$"); #22mb used (why?)
#USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
#alt 64398 0,0 0,2 33292 22700 7 S+J 22:41 0:00,03 /usr/local/bin/perl ./t.pl
substr($str, 0, 1)='';
system("ps up $$"); #No leak
#USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
#alt 64398 0,0 0,2 33292 22732 7 S+J 22:41 0:00,04 /usr/local/bin/perl ./t.pl
substr($str, 500);
system("ps up $$"); #Leaked 10Mb (why?!)
#USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
#alt 64398 0,0 0,3 43532 32520 7 S+J 22:41 0:00,05 /usr/local/bin/perl ./t.pl
my $a = substr($str, 500);
system("ps up $$"); #Leaked 10Mb + Copyed 10Mb
#USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
#alt 64398 0,0 0,5 64012 52096 7 S+J 22:41 0:00,09 /usr/local/bin/perl ./t.pl
undef $a; #Free scalar's memory
system("ps up $$"); #Free'd 10Mb
#USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
#alt 64398 0,0 0,4 53772 42308 7 S+J 22:41 0:00,09 /usr/local/bin/perl ./t.pl
# Total leaked 2 times for 10Mb each
看看substr($str, 500);
命令。除了返回字符串的副本(没关系),它还泄漏了相同数量的内存,所以如果你使用返回值,它的两次内存块中的一次会在脚本工作的整个时间内丢失...而且,似乎它不是任何类型的“内部缓冲区”,因为它泄漏了 * 每个 * 调用...
请注意,这种增加10 Mb的情况并不是“重用”内存,因为后续调用会获得越来越多的内存。
有什么建议如何解决或避免?
我的Perl 5.14.2版;我在工作中也有同样的行为(5.8.8)
3条答案
按热度按时间oxosxuxt1#
如果在分配
$str
之前进行ps
检查,您会发现仅运行perl就需要大约2兆字节,因此分配$str
就需要20兆字节,是所创建字符串大小的两倍。perl必须生成一个10meg的字符串,然后将其复制到$str
中。20meg。它保留分配的内存以供以后使用。substr($str, 0, 1)=''
导致$str
指向一个新的C字符串,你可以用Devel::Peek看到这一点,但是进程内存没有增加。可能它使用了从分配给'a' x 10_000_000
的内存中释放的内存。my $a = substr($str, 500);
也有类似的问题。substr
生成一个新的10兆字符串,然后将其复制到需要20兆的$a
中。为什么需要更多的系统内存来完成此操作我不确定。可能是perl从操作系统获得的前一个10兆块中分配了内存,因此不再是一个10兆块,它不得不向操作系统请求更多内存。undef $a
明确清除了与$a
关联的C字符串,您可以使用Devel::Peek看到,但perl不一定会将内存释放回操作系统。这是我最好的猜测。
长话短说,当内存被一个进程释放回操作系统时是很复杂的,操作系统的处理方式也不一样。
doinxwow2#
根据perlglossary,在Perl内部有一个叫做scratchpad的东西:
对特定文件或子例程的特定调用保存其某些临时值的区域,包括任何词法范围的变量。
下面是
perl -MO=Concise leak.pl
生成的代码:观察
padsv[$str:2,4]
语句。现在,如果我运行带有一些调试标志(perl -DmX leak.pl
)的代码,“leak”的来源就变得更清楚了:这只是Perl使用的暂存区。
lrl1mhuk3#
不是substr()导致内存使用加倍,而是x操作符。如果你使用vec($a,10000000,8)= 0;而不是x运算符,则只会使用正确的内存量!