2009-08-31 17 views
33

Napisałem trwałą usługę sieciową w Perlu, która działa pod Linuksem.Profilowanie pamięci Perla i wykrywanie nieszczelności Perla?

Niestety, po uruchomieniu, jego rozmiar stosu rezydentnego (RSS) rośnie, rośnie i rośnie, powoli, ale pewnie.

Jest to pomimo usilnych starań z mojej strony, aby zniszczyć wszystkie niepotrzebne klawisze mieszające i usunąć wszystkie odniesienia do obiektów, które w przeciwnym razie powodują, że liczniki odwołań pozostaną na miejscu i przeszkadzają w zbieraniu śmieci.

Czy są jakieś dobre narzędzia do profilowania wykorzystania pamięci skojarzonej z różnymi prymitywami pierwotnej danych, błogosławionymi obiektami referencyjnymi skrótu itp. W programie Perla? Czego używasz do śledzenia wycieków pamięci?

Zazwyczaj nie spędzam czasu w debugerze Perl ani w żadnym z różnych interaktywnych profilerów, więc doceniłbym ciepłą, łagodną, ​​nie-ezoteryczną odpowiedź. :-)

+0

Zrozumiałeś? Zgaduję, biorąc pod uwagę podane przez ciebie informacje, że jest biblioteka (wprowadzona przez dynalonik jakiegoś modułu), która jest winowajcą ... – Ether

+0

To wydaje się być kanonicznym pytaniem o "znalezienie luki pamięci", ponieważ moje odpowiedzi od innych podobne pytania zostały tutaj połączone :) Nie odpowiedziałem na jedno pytanie trzy razy; wiele wątków zostało połączonych razem w czasie. – Ether

+0

Slip of tongue ... miałeś na myśli "Resident Set Size" ... ta liczba nie ma związku ze stosem –

Odpowiedz

13

Możesz mieć odniesienie kołowe w jednym z twoich obiektów. Kiedy odbiera śmieci, aby zwolnić ten obiekt, okólnik oznacza, że ​​wszystko, do czego odnosi się to odniesienie, nigdy nie zostanie uwolnione. Możesz sprawdzić odwołania kołowe z Devel::Cycle i Test::Memory::Cycle. Jedną rzeczą, aby spróbować (chociaż to może dostać drogie w produkcji kodu, tak bym go wyłączyć, gdy flaga nie jest ustawiona debugowania) sprawdza okrągłych odniesień wewnątrz destruktora dla wszystkich obiektów:

# make this be the parent class for all objects you want to check; 
# or alternatively, stuff this into the UNIVERSAL class's destructor 
package My::Parent; 
use strict; 
use warnings; 
use Devel::Cycle; # exports find_cycle() by default 

sub DESTROY 
{ 
    my $this = shift; 

    # callback will be called for every cycle found 
    find_cycle($this, sub { 
      my $path = shift; 
      foreach (@$path) 
      { 
       my ($type,$index,$ref,$value) = @$_; 
       print STDERR "Circular reference found while destroying object of type " . 
        ref($this) . "! reftype: $type\n"; 
       # print other diagnostics if needed; see docs for find_cycle() 
      } 
     }); 

    # perhaps add code to weaken any circular references found, 
    # so that destructor can Do The Right Thing 
} 
+1

PS. Możesz osłabić istniejące odniesienie (aby umożliwić destruktorowi działanie jego magią przez cykle) z Scalar :: Util :: weaken() - http://search.cpan.org/~gbarr/Scalar-List-Utils-1.21 /lib/Scalar/Util.pm – Ether

+2

Hi Ether - wypróbowałem UNIVERSAL :: DESTROY(), uruchomiłem usługę na długo i uderzyłem w nią i nic nie dostałem. Tymczasem RSS skradał się. Cóż innego mogłoby być, gdyby nie kwestia okrągłych odniesień? –

+2

Naprawdę nie jestem pewien, jak szukanie wycieków pamięci w destruktorze przyniesie ci jakąkolwiek przysługę. Jeśli jesteś nieszczelny, DESTROY nigdy nie zostanie wywołany. –

9

Można użyj Devel::Leak, aby wyszukać wycieki pamięci. Jednak dokumentacja jest dość rzadka ... na przykład, gdzie można uzyskać referencję $ handle, aby przejść do Devel::Leak::NoteSV()? f Znajduję odpowiedź, dokonam edycji tej odpowiedzi.

Ok okazuje się, że za pomocą tego modułu jest dość prosta (kod skradziony bezwstydnie z Apache::Leak):

use Devel::Leak; 

my $handle; # apparently this doesn't need to be anything at all 
my $leaveCount = 0; 
my $enterCount = Devel::Leak::NoteSV($handle); 
print STDERR "ENTER: $enterCount SVs\n"; 

# ... code that may leak 

$leaveCount = Devel::Leak::CheckSV($handle); 
print STDERR "\nLEAVE: $leaveCount SVs\n"; 

bym umieścić jak najwięcej kod jak to możliwe w części środkowej, z czeku leaveCount jako tuż przed końcem wykonania (jeśli go masz) - po tym, jak większość zmiennych została zwolniona w miarę możliwości (jeśli nie możesz uzyskać zmiennej poza zakresem, możesz przypisać do niej undef, aby zwolnić cokolwiek wskazywało).

+0

zmienne są niekonsekwentnie używane w przykładzie, np. $ leave vs $ leaveCount – Lot105

4

Co dalej próbować (nie wiem, czy to będzie najlepiej umieścić w komentarzu po pytaniu powyżej choć Alexa): Co bym spróbować następnym (innego niż Devel :: Leak):

spróbować wyeliminować " niepotrzebne "części twojego programu lub segmentuj go na oddzielne pliki wykonywalne (mogą używać sygnałów do komunikowania się lub wywoływać się nawzajem z argumentami wiersza poleceń) - celem jest sprowadzanie pliku wykonywalnego do najmniejszej ilości kodu, który nadal wykazuje złe zachowanie. Jeśli jesteś pewien, że to nie twój kod to robi, zmniejsz liczbę modułów zewnętrznych, które używasz, szczególnie te, które mają implementację XS. Jeśli może to swój własny kod, szukać czegoś potencjalnie podejrzany:

  • zdecydowanie Jakiekolwiek użycie Inline :: C lub XS kodu
  • bezpośredniego wykorzystania odniesienia, na przykład\@list lub , zamiast wstępnych alokacji odniesień, takich jak [qw (foo bar)] (pierwszy z nich tworzy inne odniesienie, które może się zagubić, w drugim jest tylko jedno odniesienie do martwienia się, które jest zwykle przechowywane w lokalnym skalniku leksykalnym
  • manipulowania zmiennymi pośrednio, np $$foo gdzie $foo jest modyfikowane, co może powodować autovivication zmiennych (choć trzeba wyłączyć strict 'refs' sprawdzanie)
2

Niedawno użyłem NYTProf jako profiler dla dużej aplikacji Perla. Nie śledzi użycia pamięci, ale śledzi wszystkie wykonane ścieżki kodu, co pomaga w wykryciu źródła wycieków. Jeśli to, co wyciekasz, to rzadkie zasoby, takie jak połączenia z bazami danych, śledzenie, gdzie są przydzielane i zamykane, prowadzi długą drogę w kierunku znalezienia wycieków.