您正在使用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);
命令。另外它的字符串的返回副本(那没关系),它泄漏了相同数量的内存,所以如果你使用返回值它的两块内存,其中一个丢失了整个时间脚本工作......而且,似乎它不是任何一种“内部缓冲区”,因为它泄漏每个调用..
注意这种增加10Mb的情况不是“重用”内存,因为后续调用会获得越来越多的内存。
有任何建议如何解决或避免这种情况?
My Perl版本5.14.2;我的工作相同的行为(5.8.8)
答案 0 :(得分:2)
如果你在分配ps
之前坚持$str
检查,你会发现只有2兆用于运行perl。因此,分配$str
需要20兆,是正在创建的字符串的两倍。如果我不得不猜测发生了什么,perl必须制作一个10兆字符串,然后将其复制到$str
。 20兆。它保留在以后使用的分配内存。
substr($str, 0, 1)=''
导致$str
指向新的C字符串,您可以使用Devel :: Peek查看此内容,但不会增加进程内存。它可能会使用释放的内存来为'a' x 10_000_000
分配内存。
my $a = substr($str, 500);
也存在类似问题。 substr
创建一个新的10兆字符串,然后将其复制到需要20兆的$a
。为什么需要更多的系统内存才能做到这一点我不确定。它可以从操作系统获得的前一个10兆字节块中分配内存,因此不再是单个10兆字节块,它不得不向操作系统询问更多内容。
undef $a
肯定会清除与$a
关联的C字符串,你可以看到Devel :: Peek,但perl不一定会将内存释放回操作系统。
无论如何,这是我最好的猜测。
简而言之,当一个进程将内存释放回操作系统时,操作系统很复杂,操作系统也会采用不同的方式。这是one discussion specifically about perl和one about Linux。
答案 1 :(得分:2)
根据perlglossary深入Perl内部,有一个名为 scratchpad 的东西:
特定文件或子例程的特定调用保留其某些临时值的区域,包括任何词法范围的变量。
以下是perl -MO=Concise leak.pl
生成的代码:
...
10 <;> nextstate(main 3 leak.pl:30) v:*,&,{,x*,x&,x$,$ ->11
15 <2> sassign vKS/2 ->16
13 <@> substr[t16] sK/2 ->14
- <0> ex-pushmark s ->11
11 <0> padsv[$str:2,4] s ->12
12 <$> const(IV 500) s ->13
14 <0> padsv[$a:3,4] sRM*/LVINTRO ->15
16 <;> nextstate(main 4 leak.pl:35) v:*,&,{,x*,x&,x$,$ ->17
...
观察padsv[$str:2,4]
声明。
现在,如果我使用一些调试标志(perl -DmX leak.pl
)运行代码,“泄漏”的来源就会变得更加清晰:
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
stas 55970 1.5 0.3 2454528 21548 s001 S+ 6:11PM 0:00.04 perl -DmX leak.pl
...
Pad 0x7f8a328062c8[0x7f8a3240d040] sv: 16 sv=0x7f8a32833298
0x7f8a3240a2a0: (02222) free
0x10d013000: (02223) malloc 9999500 bytes
...
Pad 0x7f8a328062c8[0x7f8a3240d040] sv: 15 sv=0x7f8a328332c8
0x7f8a3240a560: (02231) free
0x10d99d000: (02232) malloc 9999500 bytes
...
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
stas 55970 1.5 0.5 2474064 41084 s001 S+ 6:11PM 0:00.06 perl -DmX leak.pl
所以,这只是使用暂存器的Perl。