Perl substr内存泄漏

eqoofvh9  于 2022-11-15  发布在  Perl
关注(0)|答案(3)|浏览(124)

嗨,我在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)

oxosxuxt

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不一定会将内存释放回操作系统。
这是我最好的猜测。
长话短说,当内存被一个进程释放回操作系统时是很复杂的,操作系统的处理方式也不一样。

doinxwow

doinxwow2#

根据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)的代码,“leak”的来源就变得更清楚了:

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使用的暂存区。

lrl1mhuk

lrl1mhuk3#

不是substr()导致内存使用加倍,而是x操作符。如果你使用vec($a,10000000,8)= 0;而不是x运算符,则只会使用正确的内存量!

相关问题