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:
- 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?
- 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ę.
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