Wycieki pamięci w Pythonie [zamknięte]


180

Mam długo działający skrypt, który, jeśli zostanie uruchomiony wystarczająco długo, zajmie całą pamięć w moim systemie.

Nie wdając się w szczegóły scenariusza, mam dwa pytania:

  1. Czy są jakieś „sprawdzone metody”, których należy przestrzegać, które pomogą zapobiegać wyciekom?
  2. Jakie są techniki debugowania wycieków pamięci w Pythonie?

5
Uważam, że ten przepis jest pomocny.
David Schein

Wydaje się, że wypisuje zbyt dużo danych, aby były użyteczne
Casebash

1
@Casebash: Jeśli ta funkcja drukuje cokolwiek, poważnie robisz to źle. Zawiera listę obiektów z __del__metodami, do których nie ma już odniesień, z wyjątkiem ich cyklu. Cyklu nie można przerwać z powodu problemów z __del__. Napraw to!
Helmut Grohne

Odpowiedzi:



83

Wypróbowałem większość wymienionych wcześniej opcji, ale ten mały i intuicyjny pakiet okazał się najlepszy: pympler

Śledzenie obiektów, które nie zostały zebrane jako śmieci, jest dość proste, sprawdź ten mały przykład:

zainstaluj pakiet za pośrednictwem pip install pympler

from pympler.tracker import SummaryTracker
tracker = SummaryTracker()

# ... some code you want to investigate ...

tracker.print_diff()

Dane wyjściowe pokazują wszystkie dodane obiekty oraz ilość zużytej przez nie pamięci.

Przykładowe dane wyjściowe:

                                 types |   # objects |   total size
====================================== | =========== | ============
                                  list |        1095 |    160.78 KB
                                   str |        1093 |     66.33 KB
                                   int |         120 |      2.81 KB
                                  dict |           3 |       840 B
      frame (codename: create_summary) |           1 |       560 B
          frame (codename: print_diff) |           1 |       480 B

Ten pakiet zawiera szereg dodatkowych funkcji. Zapoznaj się z dokumentacją pymplera , w szczególności z rozdziałem Identyfikacja wycieków pamięci .


5
Warto zauważyć, że pymplermoże to być WOLNE . Jeśli robisz coś częściowo w czasie rzeczywistym, może to całkowicie sparaliżować wydajność aplikacji.
Fałszywe nazwisko

@sebpiq dziwnie, to samo dzieje się ze mną ... czy masz pojęcie, dlaczego tak się dzieje? Szybkie spojrzenie na kod źródłowy nie dało prawdziwego wglądu.
linusg

25

Polecam narzędzie mem_top , które stworzyłem

Pomogło mi to rozwiązać podobny problem

Po prostu natychmiast pokazuje głównych podejrzanych o wycieki pamięci w programie w języku Python


1
to prawda ... ale daje bardzo niewiele w sposobie wyjaśnienia użycia / wyników
me_

@me_, to narzędzie ma udokumentowane sekcje „Użycie” i „Objaśnienie wyników”. Czy powinienem dodać wyjaśnienie typu „refs to liczba referencji z obiektu, typy to liczba obiektów tego typu, bajty to rozmiar obiektu” - czy nie byłoby to zbyt oczywiste, aby to udokumentować?
Denis Ryzhkov,

dokumentacja użycia narzędzia zawiera jeden wiersz z napisem „od czasu do czasu: logging.debug (mem_top ())”, podczas gdy wyjaśnienie wyników jest prawdziwym doświadczeniem autora w śledzeniu błędów bez kontekstu ... to nie jest specyfikacja techniczna, która mówi programista dokładnie tego, na co patrzą ... Nie wybijam twojej odpowiedzi ... pokazuje, że podejrzani na wysokim poziomie są rozliczeni ... nie zapewnia odpowiedniej dokumentacji, aby w pełni zrozumieć wynik użycia ... na przykład , w danych wyjściowych „Wyjaśnienie wyników”, dlaczego „GearmanJobRequest” jest oczywiście problemem? brak wyjaśnienia, dlaczego ...
me_

1
Myślę, że nieumyślnie pukam twoje narzędzie, jesteś autorem ... bez obrazy nie było zamierzone ...
me_

6
@me_, właśnie dodałem następny krok do "Użycie", dodałem sekcję "Liczniki", dodałem wyjaśnienie, dlaczego dokładnie Gearman był podejrzanym w tym prawdziwym przykładzie, udokumentowałem każdy opcjonalny parametr "mem_top ()" w kodzie, i przesłał to wszystko jako v0.1.7 - zobacz, czy można coś jeszcze ulepszyć. Dziękuję Ci! )
Denis Ryzhkov,

18

Moduł Tracemalloc został zintegrowany jako moduł wbudowany począwszy od Pythona 3.4 i najwyraźniej jest również dostępny dla wcześniejszych wersji Pythona jako biblioteka innej firmy (chociaż go nie testowałem).

Ten moduł jest w stanie wyświetlać dokładne pliki i wiersze, które przydzieliły najwięcej pamięci. IMHO, ta informacja jest nieskończenie cenniejsza niż liczba przydzielonych instancji dla każdego typu (co kończy się na wielu krotkach w 99% przypadków, co jest wskazówką, ale w większości przypadków ledwo pomaga).

Polecam użycie tracemalloc w połączeniu z piryzytem . 9 razy na 10, uruchomienie 10 najlepszych fragmentów w powłoce pirazytu dostarczy Ci wystarczających informacji i wskazówek, jak naprawić wyciek w ciągu 10 minut. Jeśli jednak nadal nie możesz znaleźć przyczyny wycieku, skorupa pirazytu w połączeniu z innymi narzędziami wymienionymi w tym wątku prawdopodobnie da ci więcej wskazówek. Powinieneś także przyjrzeć się wszystkim dodatkowym pomocnikom zapewnianym przez pirasyt (takim jak przeglądarka pamięci).



12

W szczególności powinieneś spojrzeć na swoje globalne lub statyczne dane (dane długo żyjące).

Kiedy te dane rosną bez ograniczeń, możesz również mieć problemy w Pythonie.

Garbage collector może zbierać tylko dane, do których już się nie odwołuje. Jednak dane statyczne mogą łączyć elementy danych, które powinny zostać zwolnione.

Innym problemem mogą być cykle pamięci, ale przynajmniej w teorii Garbage collector powinien znaleźć i wyeliminować cykle - przynajmniej tak długo, jak długo nie są one uzależnione od jakichś długowiecznych danych.

Jakie rodzaje długowiecznych danych są szczególnie kłopotliwe? Przyjrzyj się dobrze listom i słownikom - mogą rosnąć bez ograniczeń. W słownikach możesz nawet nie zauważyć nadchodzących problemów, ponieważ kiedy korzystasz z dyktowania, liczba kluczy w słowniku może nie być dla ciebie zbyt widoczna ...



4

Jeśli chodzi o najlepsze praktyki, miej oko na funkcje rekurencyjne. W moim przypadku napotkałem problemy z rekurencją (gdzie nie było takiej potrzeby). Uproszczony przykład tego, co robiłem:

def my_function():
    # lots of memory intensive operations
    # like operating on images or huge dictionaries and lists
    .....
    my_flag = True
    if my_flag:  # restart the function if a certain flag is true
        my_function()

def main():
    my_function()

działanie w ten sposób rekurencyjny nie wyzwoli wyrzucania elementów bezużytecznych i nie usunie pozostałości funkcji, więc za każdym razem użycie pamięci rośnie i rośnie.

Moim rozwiązaniem było wyciągnięcie rekurencyjnego wywołania z funkcji my_function () i posiadanie uchwytu main (), kiedy wywołać je ponownie. w ten sposób funkcja kończy się naturalnie i oczyszcza po sobie.

def my_function():
    # lots of memory intensive operations
    # like operating on images or huge dictionaries and lists
    .....
    my_flag = True
    .....
    return my_flag

def main():
    result = my_function()
    if result:
        my_function()

7
Używanie rekurencji w ten sposób również nie powiedzie się, jeśli przekroczysz limit głębokości rekurencji, ponieważ Python nie optymalizuje wywołań ogona. Domyślnie jest to 1000 wywołań rekurencyjnych.
Lie Ryan

3

Nie jestem pewien co do „Najlepszych praktyk” dotyczących wycieków pamięci w Pythonie, ale Python powinien wyczyścić swoją własną pamięć przez swój moduł odśmiecania pamięci. Więc głównie zacząłbym od sprawdzania okólnej listy jakichś krótkich, ponieważ nie zostaną one odebrane przez śmieciarza.


3
lub odniesienia do obiektów, które są przechowywane na zawsze, itp.
Matt b

3
Czy możecie podać przykłady okrągłych list i obiektów, które są przechowywane na zawsze?
Daniel

2

Nie jest to bynajmniej rada wyczerpująca. Ale najważniejszą rzeczą, o której należy pamiętać, pisząc z myślą o unikaniu przyszłych wycieków pamięci (pętli), jest upewnienie się, że wszystko, co akceptuje odwołanie do wywołania zwrotnego, powinno przechowywać to wezwanie jako słabe odniesienie.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.