2009-09-16 9 views
112

Mam długotrwały skrypt, który, jeśli pozwoli działać wystarczająco długo, pochłonie całą pamięć w moim systemie.Wycieki w pamięci Pythona

Bez wchodzenia w szczegóły na temat scenariusza, mam dwa pytania:

  1. Czy istnieją jakieś „Dobre Praktyki” do naśladowania, które pomogą zapobiec wyciekom z występujących?
  2. Jakie są techniki debugowania pamięci w Pythonie?
+3

Znalazłem [ten przepis] (http://code.activestate.com/recipes/65333/) pomocne. –

+0

Wydaje się wydrukować zbyt wiele danych, aby były przydatne – Casebash

+1

@Casebash: jeśli ta funkcja wypisze coś, co robisz, robisz to źle. Listuje obiekty z metodą "__del__", które nie są już przywoływane z wyjątkiem ich cyklu. Cykl nie może zostać przerwany z powodu problemów z '__del__'. Napraw to! –

Odpowiedz

4

Nie jestem pewien co do "najlepszych praktyk" dotyczących wycieków pamięci w pythonie, ale python powinien wyczyścić swoją pamięć za pomocą modułu do usuwania śmieci. Zacznę więc od sprawdzenia krótkiej listy, ponieważ nie będą one zbierane przez odśmiecacz.

+3

lub odwołania do obiektów, które są przechowywane na zawsze, itd. –

+0

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

8

Powinieneś szczególnie rzucić okiem na swoje dane globalne lub statyczne (dane długoterminowe).

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

Śmieciarka może gromadzić tylko dane, do których nie ma już odniesienia. Ale statyczne dane mogą łączyć elementy danych, które powinny zostać zwolnione.

Innym problemem mogą być cykle pamięci, ale przynajmniej teoretycznie Garbage collector powinien znaleźć i wyeliminować cykle - przynajmniej tak długo, jak długo nie są one zawieszone na niektórych długich żywych danych.

Jakie dane dotyczące długiego życia są szczególnie kłopotliwe? Zapoznaj się dobrze z listami i słownikami - mogą one rosnąć bez ograniczeń. W słownikach możesz nawet nie widzieć problemów, które pojawią się od czasu, gdy masz dostęp do dyktatur, liczba kluczy w słowniku może nie być dla ciebie zbyt duża ...

3

Nie jest to wyczerpująca porada. Ale pierwszą rzeczą, o której należy pamiętać przy pisaniu z myślą o uniknięciu przyszłych wycieków pamięci (pętli), jest upewnienie się, że wszystko, co przyjmuje odniesienie do oddzwonienia, powinno przechowywać to wezwanie jako słabe odniesienie.

13

Pozwól mi polecić narzędzie mem_top,
, które pomogło mi rozwiązać podobny problem.

Po prostu natychmiast pokazuje podejrzanych o wycieki pamięci w programie Python.

+0

To prawda ... ale daje bardzo mało w sposobie wyjaśnienia użycia/wyników. –

+0

@me_ to narzędzie ma obie części "Użycie" i "Wyjaśnienie wyniku". Czy powinienem dodać wyjaśnienie takie jak "refs to liczba referencji od obiektu, typy to liczba obiektów tego typu, bajty to rozmiar obiektu" - czy nie byłoby to zbyt oczywiste, aby to udokumentować? –

+0

Dokumenty użycia narzędzia dają pojedynczą linię mówiącą "od czasu do czasu: logging.debug (mem_top())", podczas gdy wyjaśnienie wyników jest prawdziwym doświadczeniem autorskiego śledzenia błędów bez kontekstu ... to nie jest specyfikacja techniczna, która mówi dev dokładnie o tym, na co patrzy ... Nie pukam w twoją odpowiedź ... pokazuje podejrzanych wysokiego poziomu jako rachunki ... nie daje odpowiedniej dokumentacji, aby w pełni zrozumieć wynik użycia ... przykład, w wyniku "Wyjaśnienie wyników" dlaczego "GearmanJobRequest" jest oczywiście problemem? nie ma wytłumaczenia dlaczego ... –

51

Starałem się większość opcji wspomniano wcześniej, ale okazało się to małe i intuicyjny pakiet, aby być najlepszym: pympler

Jest to dość proste do obiektów, które nie były śmieci zgromadzone śladowych, sprawdzić to mały przykład:

zainstalować pakiet poprzez pip install pympler

from pympler.tracker import SummaryTracker 
tracker = SummaryTracker() 

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

tracker.print_diff() 

wyjście pokazuje wszystkie obiekty, które zostały dodane, plus pamięć one spożywane.

Przykładowe wyjście:

        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 

Pakiet ten zapewnia szereg innych funkcji. Sprawdź pympler's documentation, w szczególności sekcję Identifying memory leaks.

+1

Śmieszne rzeczy ... mój wyciek pamięci faktycznie zniknął, kiedy zacząłem używać pympler, aby spróbować go śledzić. Prawdopodobnie jakiś problem ze zbieraniem ... – sebpiq

+1

Warto zauważyć, że 'pympler' może być ** WOLNY **. Jeśli robisz coś w czasie częściowo realnym, może to całkowicie obniżyć wydajność twojej aplikacji. –

2

Jeśli chodzi o najlepsze praktyki, należy zwracać uwagę na funkcje rekurencyjne. W moim przypadku natknąłem się na problemy z rekurencją (gdzie nie było 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łających w tej rekurencyjnego sposób nie spowoduje zbieranie śmieci i usunąć resztki funkcji, więc za każdym razem dzięki wykorzystaniu pamięci rośnie i rośnie.

Moje rozwiązanie polegało na wycofaniu wywołania rekursywnego z funkcji my_function() i posiadaniu funkcji main() podczas wywoływania go ponownie. w ten sposób funkcja kończy się naturalnie i czyści 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() 
+4

Korzystanie z rekurencji w ten sposób również zostanie przerwane, jeśli osiągniesz ograniczenie głębokości rekursji, ponieważ Python nie optymalizuje wywołań typu "ogon". Domyślnie jest to 1000 wywołań rekurencyjnych. –

3

Wykrywanie i lokalizowanie wycieków pamięci w przypadku długotrwałych procesów, np. w środowiskach produkcyjnych można teraz używać stackimpact. Używa pod spodem tracemalloc. Więcej informacji w this post.

enter image description here

4

Tracemalloc module została włączona jako wbudowany moduł począwszy od Python 3.4, a appearently, jest dostępna także dla wcześniejszych wersji Pythona jako a third-party library (nie testowałem go jednak).

Ten moduł może wyprowadzać precyzyjne pliki i linie, dla których przydzielono najwięcej pamięci. IMHO, ta informacja jest nieskończenie bardziej wartościowa niż liczba alokowanych instancji dla każdego typu (co kończy się mnóstwem krotek w 99% przypadków, co jest wskazówką, ale prawie nie pomaga w większości przypadków).

Polecam używać tracemalloc w połączeniu z pyrasite. 9 razy na 10, uruchomienie top 10 snippet w pyrasite-shell da ci wystarczającą ilość informacji i wskazówek, aby naprawić wyciek w ciągu 10 minut. Jednak, jeśli nadal nie jesteś w stanie znaleźć przyczyny przecieku, pirasitowa powłoka w połączeniu z innymi narzędziami wymienionymi w tym wątku prawdopodobnie da ci jeszcze więcej wskazówek. Powinieneś również rzucić okiem na wszystkie dodatkowe pomoce dostarczone przez pirasit (takie jak przeglądarka pamięci).