Perl substr内存泄漏

时间:2012-11-28 18:55:54

标签: perl memory-leaks

您正在使用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)

2 个答案:

答案 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 perlone 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。