2016-09-15 5 views
12

Mam aplikację, która odczytuje gigantyczną porcję danych tekstowych w skalar, czasami nawet w GB. Używam substr na tym skalarku, aby odczytać większość danych w innym skalarze i zastąpić wyodrębnione dane pustym łańcuchem, ponieważ nie jest już potrzebny w pierwszym skalarze. Ostatnio odkryłem, że Perl nie zwalnia pamięci pierwszego skalara, ale rozpoznaje, że jego logiczna długość uległa zmianie. Muszę więc najpierw wyodrębnić dane z pierwszego skalaru do trzeciego, najpierw skalarować i przywrócić wyodrębnione dane z powrotem. Tylko w ten sposób pamięć zajmowana przez pierwszy skalar jest naprawdę uwolniona. Przypisanie undef do tego skalaru lub jakiejś innej wartości mniejszej niż przydzielony blok pamięci nie zmienia niczego w przydzielonej pamięci.Perl: Kiedy jest niepotrzebna pamięć skalarna uwolniona bez wychodzenia poza zakres?

Oto, co robię teraz:

 $$extFileBufferRef = substr($$contentRef, $offset, $length, ''); 
    $length   = length($$contentRef); 
    my $content   = substr($$contentRef, 0, $length); 
    $$contentRef  = undef($$contentRef) || $content; 

$$contentRef może być na przykład 5 GB w pierwszym wierszu, wydobywam 4,9 GB danych i zastępuję wyodrębnione dane. Drugi wiersz będzie teraz raportować np. 100 MB danych jako długość ciągu, ale np. Devel::Size::total_size nadal będzie wyświetlać dane o 5 GB danych dla tego skalaru. I przypisanie undef lub podobnym do $$contentRef nie wydaje się, że coś w tym zmieni, muszę wywołać undef jako funkcję tego skalara.

Spodziewałbym się, że pamięć za $$contentRef została już przynajmniej częściowo zwolniona po zastosowaniu substr. Wygląda na to, że ...

Czy pamięć jest zwalniana tylko wtedy, gdy zmienne wykraczają poza zakres? A jeśli tak, dlaczego przypisanie undef różni się od wywoływania undef jako funkcji na tym samym skalarnym?

+3

Czy ** potrzebujesz ** tej pamięci w innym miejscu? – simbabque

+0

Tak, mam wiele kopii danych z różnych powodów, a ponadto cały proces może być wykonywany równolegle. Więc niektóre zmarnowane GB pamięci podczas całego procesu są czymś, o co muszę dbać. I owszem, może to być zły projekt i wszystko, ale tak właśnie jest obecnie ... –

Odpowiedz

13

Twoja analiza jest poprawna.

$ perl -MDevel::Peek -e' 
    my $x; $x .= "x" for 1..100; 
    Dump($x); 
    substr($x, 50, length($x), ""); 
    Dump($x); 
' 
SV = PV(0x24208e0) at 0x243d550 
    ... 
    CUR = 100  # length($x) == 100 
    LEN = 120  # 120 bytes are allocated for the string buffer. 

SV = PV(0x24208e0) at 0x243d550 
    ... 
    CUR = 50  # length($x) == 50 
    LEN = 120  # 120 bytes are allocated for the string buffer. 

Nie tylko Perl overallocate struny, to nie ma nawet darmowe zmienne, które wykraczają poza zakres, zamiast ponowne im następnym razem zakres został wprowadzony.

$ perl -MDevel::Peek -e' 
    sub f { 
     my ($set) = @_; 
     my $x; 
     if ($set) { $x = "abc"; $x .= "def"; } 
     Dump($x); 
    } 

    f(1); 
    f(0); 
' 
SV = PV(0x3be74b0) at 0x3c04228 # PV: Scalar may contain a string 
    REFCNT = 1 
    FLAGS = (POK,pPOK)    # POK: Scalar contains a string 
    PV = 0x3c0c6a0 "abcdef"\0  # The string buffer 
    CUR = 6 
    LEN = 10      # Allocated size of the string buffer 

SV = PV(0x3be74b0) at 0x3c04228 # Could be a different scalar at the same address, 
    REFCNT = 1      # but it's truly the same scalar 
    FLAGS =()      # No "OK" flags: undef 
    PV = 0x3c0c6a0 "abcdef"\0  # The same string buffer 
    CUR = 6 
    LEN = 10      # Allocated size of the string buffer 

Logika jest taka, że ​​jeśli potrzebne pamięć raz, istnieje duża szansa, będziesz go potrzebować ponownie.

Z tego samego powodu przypisanie undef do skalaru nie zwalnia jego bufora ciągów. Ale Perl daje ci szansę na uwolnienie buforów, jeśli chcesz, więc przekazanie skalaru do undef wymusza uwolnienie wewnętrznych buforów skalarnych.

$ perl -MDevel::Peek -e' 
    my $x = "abc"; $x .= "def"; Dump($x); 
    $x = undef;     Dump($x); 
    undef $x;     Dump($x); 
' 
SV = PV(0x37d1fb0) at 0x37eec98 # PV: Scalar may contain a string 
    REFCNT = 1 
    FLAGS = (POK,pPOK)    # POK: Scalar contains a string 
    PV = 0x37e8290 "abcdef"\0  # The string buffer 
    CUR = 6 
    LEN = 10      # Allocated size of the string buffer 

SV = PV(0x37d1fb0) at 0x37eec98 # PV: Scalar may contain a string 
    REFCNT = 1 
    FLAGS =()      # No "OK" flags: undef 
    PV = 0x37e8290 "abcdef"\0  # The string buffer is still allcoated 
    CUR = 6 
    LEN = 10      # Allocated size of the string buffer 

SV = PV(0x37d1fb0) at 0x37eec98 # PV: Scalar may contain a string 
    REFCNT = 1 
    FLAGS =()      # No "OK" flags: undef 
    PV = 0       # The string buffer has been freed. 
+0

Dzięki, nie zdawałem sobie sprawy z tego poza zakresem zachowania. Czy to podejście do buforowania dla procesu OS lub interpretera Perla? Ponieważ używam mod_perl i uznałem, że duże ilości pamięci są przechowywane w procesie po żądaniu. Myśl o wycieku pamięci gdzieś, ale może być to "sprytne" buforowanie. Proces może mieć wielu interpretatorów w pamięci i jeśli wszystkie te buforują niektóre GB danych, mam problem. –

+0

Niestety, nie sądzę, że rozumiem twoją odpowiedź: wiele wątków na proces wybiera arbitralnych tłumaczy dostępnych w pamięci w puli w mod_perl, jeśli mam rację.Pamięć buforowana/przydzielona musi być przypisana do interpretera i jest używana tylko wtedy, gdy jakiś wątek wykonuje jakiś interpreter z taką pamięcią buforowaną/przydzieloną? Inni tłumacze nie odnoszą korzyści, ale zamiast tego przydzielają siebie. Wątek opuszczający interpreter nie zwalnia pamięci. 10 tłumaczy, 10 * 2 GB danych przydzielonych na przykład. Usuwane tylko wtedy, gdy tłumacze są usuwani z procesu. Dobrze? –

+0

- interpretery mod_perl nie są zwalniane po użyciu, pozostają w pamięci i są domyślnie używane wielokrotnie przez różne wątki. Sama mod_perl nie miałaby sensu, ponieważ nie uzyskasz żadnego wyniku, jeśli wszyscy tłumacze zostaną uwolnieni bezpośrednio po żądaniu. Kod musiałby być cały czas kompilowany ... Nawet dokumenty mówią inaczej: https://perl.apache.org/docs/2.0/user/config/config.html#Threads_Mode_Specific_directives –

Powiązane problemy