2012-11-28 8 views
5

Hi ich arbeite mit großen String-Daten in Perl-Variable (seine Roh-E-Mail-Körper, so dass es Anhänge enthalten kann). Interessantes Problem mit Perls substr. Scheint sein Leck oder etwas falsch zu machen (wenn ja, was?). Code beachten:Perl substr Speicherleck

#!/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 

Betrachten Sie substr($str, 500); Befehl. Zusätzlich die Kopie der Zeichenfolge zurück (das ist in Ordnung), es leckt die gleiche Menge an Speicher, so dass, wenn Sie Rückgabewert seine doppelte Stück Speicher, von denen eine für die ganze Zeit Skript funktioniert verloren ist ... Auch scheint es nicht irgendwelche Art „interne Puffer“, da es jeden Anruf ..

Hinweis: dieser Fall von 10Mb Anstieg Lecks ist nicht „Wieder nützlich“ Speicher da nachfolgende Anrufe mehr und mehr Speicher erhalten ..

Irgendwelche Vorschläge, wie um das zu beheben oder zu vermeiden?

Meine Perl-Version 5.14.2; Gleiches Verhalten, das ich auf meine Arbeit bekam (5.8.8)

+0

Interessant, scheint das Leck zu Perl v5.12.4 & v5.14.2 passieren, aber nicht v5.16.2. – creaktive

+0

Aktualisiert über die Version – PSIAlt

+0

Der beste Teil: selbst auf v5.16.2, 'undef' scheint nicht den Speicher freizugeben! – creaktive

Antwort

2

Wenn Sie einen ps Scheck vor dem Zuweisen $str überprüfen, finden Sie ungefähr 2 MBs wird gerade verwendet, um Perl zu laufen. Die Zuweisung von $str dauert also 20 MB, doppelt so groß wie die zu erstellende Zeichenfolge. Wenn ich raten müsste, was passiert, muss Perl eine 10-Megabit-Zeichenfolge erstellen und sie dann in $str kopieren. 20 MB. Es enthält den zugewiesenen Speicher, den Sie später verwenden können.

substr($str, 0, 1)='' Ursachen $str auf einen neuen C-String zeigen, können Sie dies mit Devel :: Peek sehen, aber der Prozessspeicher ist nicht erhöht. Es ist möglich, dass es den Speicher verwendete, der von der Zuweisung des Speichers für 'a' x 10_000_000 freigegeben wurde.

my $a = substr($str, 500); hat ein ähnliches Problem. Die substr erstellt eine neue 10-Megabit-Zeichenfolge und kopiert sie dann in $a mit 20 MB. Warum es mehr Systemspeicher braucht, um das zu tun, bin ich nicht sicher. Es ist möglich, dass perl allozierten Speicher von den vorherigen 10-Megabyte-Chunks, die es vom Betriebssystem bekam, zugewiesen wurden und somit kein einziger 10-Meg-Chunk mehr vorhanden war und es das Betriebssystem nach mehr fragen musste.

undef $a definitiv löscht die C-Zeichenfolge im Zusammenhang mit $a, können Sie mit Devel :: Peek sehen, aber Perl muss nicht unbedingt den Speicher zurück zum Betriebssystem freigeben.

Das ist meine beste Schätzung sowieso.

Lange Rede, kurzer Sinn, wenn Speicher durch einen Prozess zurück zum Betriebssystem freigegeben wird, ist kompliziert und Betriebssysteme tun es anders. Hier ist one discussion specifically about perl und one about Linux.

+0

Im Vergleich zur typischen XS-Funktion: XS-Funktionen geben in der Regel sv2mortal-Skalar (Referenz ist schwächen) zurück, wenn ich $ a = func() nehme; wird sein refcount erhöht (keine Kopie) oder SV wird kopiert (erstes SV gelöscht, weil recount schwächt). Aber auf 'substr (string, num)' passiert es selbst im leeren Kontext nicht. Das ist das Seltsamste für mich; es füllt nicht beide Fälle – PSIAlt

2

Nach perlglossary, tief vergraben in Perl-Interna, gibt es eine thingie genannt Scratchpad:

Der Bereich, in dem ein bestimmter Aufruf einer bestimmten Datei oder ein Unterprogramm einen Teil seiner temporären Werte hält, einschließlich alle lexikalisch begrenzten Variablen.

Hier ist der Code generiert durch 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 
... 

Beachten Sie die padsv[$str:2,4] Aussage. Nun, wenn ich den Code mit einigen Debug-Flags (perl -DmX leak.pl) laufen, wird die Quelle der "Leck" deutlicher:

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 

Also, das ist nur Perl das Scratchpad verwenden.

+0

Also warum jeder Aufruf von 'substr ($ str, 500); essen 10mb zusätzlichen Speicher? Auf Ihrem Post scheint es 10 MB im Notizblock zu reservieren und es wiederzuverwenden, aber der Speicher ist bei jedem Anruf verloren. – PSIAlt

+0

@PSIAlt Entschuldigung, ich habe vergessen, die 'padsv [$ a: 3,4]' Zeile zu erwähnen, die das ist Quelle der zweiten Scratchpad-Zuweisung! – creaktive

+0

Warum wies der zweite Aufruf von substr den vorher zugewiesenen Speicher nicht wieder im selben Scratchpad auf? – PSIAlt