2013-06-04 16 views
6

Jedna z moich aplikacji Pythona wydaje się przeciekać pamięć, sądząc po stale rosnącym zużyciu pamięci. Moja hipoteza jest gdzieś w cyklu, mimo najlepszych starań, aby tego uniknąć. Aby wyizolować problem, szukam sposobów ręcznego sprawdzania nieosiągalnych elementów, narzędzia skierowanego wyłącznie na debugowanie.problem ze zrozumieniem Pythona gc.garbage (do śledzenia wycieków pamięci)

Moduł gc wydaje się być zdolny do niezbędnego śledzenia, a ja spróbowałem następującego kodu, który ma na celu skompilowanie listy elementów nienadających się do wyłączenia, które powstały od czasu ostatniego połączenia. Pierwsze wywołanie określa jedynie podstawowy punkt kontrolny i nie rozpoznaje nieosiągalnych elementów.

def unreachable(): 
    # first time setup 
    import gc 
    gc.set_threshold(0) # only manual sweeps 
    gc.set_debug(gc.DEBUG_SAVEALL) # keep unreachable items as garbage 
    gc.enable() # start gc if not yet running (is this necessary?) 
    # operation 
    if gc.collect() == 0: 
    return 'no unreachable items' 
    s = 'unreachable items:\n ' \ 
    + '\n '.join('[%d] %s' % item for item in enumerate(gc.garbage)) 
    _deep_purge_list(gc.garbage) # remove unreachable items 
    return s # return unreachable items as text 

Tutaj, _deep_purge_list ma na celu złamanie cykli i ręczne usunięcie obiektów. Poniższa implementacja obsługuje niektóre typowe przypadki, ale nie jest w pobliżu wody. Moje pierwsze pytanie dotyczy tego, patrz poniżej.

def _deep_purge_list(garbage): 
    for item in garbage: 
    if isinstance(item, dict): 
     item.clear() 
    if isinstance(item, list): 
     del item[:] 
    try: 
     item.__dict__.clear() 
    except: 
     pass 
    del garbage[:] 

W oparciu o bardzo ograniczone testy układ wydaje się działać poprawnie. Poniższy odniesienie cykliczne raporty poprawnie raz:

class A(object): 
    def __init__(self): 
    self.ref = self 

print unreachable() 
# no unreachable items 

A() 

print unreachable() 
# unreachable items: 
# [0] <__main__.A object at 0xb74579ac> 
# [1] {'ref': <__main__.A object at 0xb74579ac>} 

print unreachable() 
# no unreachable items 

Jednak z następujących coś dziwnego się dzieje:

print unreachable() 
# no unreachable items 

import numpy 

print unreachable() 
# unreachable items: 
# [0] (<type '_ctypes.Array'>,) 
# [1] {'__module__': 'numpy.ctypeslib', '__dict__': <attribute '__dict__' of 'c_long_Array_1' objects>, '__weakref__': <attribute '__weakref__' of 'c_long_Array_1' objects>, '_length_': 1, '_type_': <class 'ctypes.c_long'>, '__doc__': None} 
# [2] <class 'numpy.ctypeslib.c_long_Array_1'> 
# [3] <attribute '__dict__' of 'c_long_Array_1' objects> 
# [4] <attribute '__weakref__' of 'c_long_Array_1' objects> 
# [5] (<class 'numpy.ctypeslib.c_long_Array_1'>, <type '_ctypes.Array'>, <type '_ctypes._CData'>, <type 'object'>) 

print unreachable() 
# unreachable items: 
# [0] (<type '_ctypes.Array'>,) 
# [1] {} 
# [2] <class 'c_long_Array_1'> 
# [3] (<class 'c_long_Array_1'>, <type '_ctypes.Array'>, <type '_ctypes._CData'>, <type 'object'>) 

Powtarzające inwokacje powracają ten ostatni wynik. Problem nie występuje, gdy nieosiągalny wywoływany jest po raz pierwszy po imporcie. Jednak w tym momencie nie mam powodu sądzić, że ten problem jest specyficzny; Domyślam się, że ujawnia to błąd w moim podejściu.

Moje pytania:

  1. Czy istnieje lepszy sposób na usunięcie elementów w gc.garbage? Idealnie, czy jest jakiś sposób na usunięcie gc, tak jakby (powinien?) Zrobić bez DEBUG_SAVEALL?
  2. Czy ktoś może wyjaśnić problem z importem numpy i/lub sugerują sposoby jego rozwiązania?

Refleksja:

Wydaje się, że kod poniżej wykonuje blisko przeznaczone:

def unreachable(): 
    import gc 
    gc.set_threshold(0) 
    gc.set_debug(gc.DEBUG_LEAK) 
    gc.enable() 
    print 'collecting {{{' 
    gc.collect() 
    print '}}} done' 

Jednak do debugowania wolę bogate reprezentacje ciąg nad typu/ID dostarczone przez GC. Ponadto chciałbym zrozumieć wadę mojego poprzedniego podejścia i dowiedzieć się czegoś o module gc.

Doceniając pomoc,

Gertjan

Aktualizacja 06/05:

Pobiegłem do sytuacji, w której pierwsze wdrożenie nie wystąpiły niedostępnych przedmiotów, chyba że miejscowi() została wywołana tuż przed do niego (odrzucając wartość zwracaną). Nie rozumienie, w jaki sposób może to wpłynąć na śledzenie obiektów w GC, pozostawia mnie jeszcze bardziej zdezorientowanym. Nie jestem pewien, jak łatwo będzie skonstruować mały przykład, który demonstruje ten problem, ale jeśli żądanie go wzywa, mogę dać mu szansę.

+0

Pomyślałem tylko o tym, ale być może źle zrozumiałeś "gc.DEBUG_SAVEALL": jest on przeznaczony do dodawania uwolnionych obiektów do 'gc.garbage' (zamiast tylko ich zwalniania). – blueyed

Odpowiedz

0

Ostatnim razem, gdy miałem taką potrzebę, skończyłem używając do tego celu objgraph module. Daje to o wiele dokładniejsze informacje, niż łatwo można uzyskać bezpośrednio z urządzenia gc module. Niestety nie mam pod ręką żadnego kodu ilustrującego jego użycie.

Miejsce, w którym upada, znajduje się w pamięci przydzielonej przez dowolne biblioteki kodu C nazwane. Na przykład, jeśli projekt wykorzystuje PIL, bardzo łatwo jest przeciekać pamięć z powodu niepoprawnego wypuszczania obiektów Pythona, które są wspierane przez dane C. Od modułu C zależy sposób prawidłowego zamknięcia takich obiektów.

+0

Cześć Samantha, dziękuję, tak Zgadzam się, objgraph to świetne narzędzie do śledzenia referencji obiektu. Nie mogłem jednak wymyślić, jak go użyć, aby podać listę przedmiotów, których nie można wyłączyć, w taki sposób, że "A" pojawia się w moim przykładzie; eksperymenty z get_leaking_objects zakończyły się niepowodzeniem. W swoim blogu Marius wyjaśnia wycieki pamięci na przykładzie odniesień z obiektów globalnych, więc przekonano mnie, że objgraph może nie nadawać się do śledzenia cyklicznych odniesień. Jeśli ten wniosek jest błędny, najbardziej interesuje mnie, w jaki sposób można je odkryć. – gertjan

Powiązane problemy