2010-02-02 10 views
12

Piszę niestandardowy przeszukiwacz systemu plików, który przekazuje miliony globów do przetworzenia przez sys.stdin. Zauważyłem, że podczas uruchamiania skryptu jego wykorzystanie pamięci znacznie wzrasta wraz z upływem czasu, a cała operacja praktycznie się zatrzymuje. Poniżej napisałem minimalny przypadek, który pokazuje problem. Czy robię coś złego, czy też znalazłem błąd w Pythonie/module glob? (Używam Pythona 2.5.2).Dlaczego mam wyciek pamięci z tej pętli python?


#!/usr/bin/env python 
import glob 
import sys 
import gc 

previous_num_objects = 0 

for count, line in enumerate(sys.stdin): 
    glob_result = glob.glob(line.rstrip('\n')) 
    current_num_objects = len(gc.get_objects()) 
    new_objects = current_num_objects - previous_num_objects 

    print "(%d) This: %d, New: %d, Garbage: %d, Collection Counts: %s"\ 
% (count, current_num_objects, new_objects, len(gc.garbage), gc.get_count()) 
    previous_num_objects = current_num_objects 

Wyjście wygląda następująco:

 
(0) This: 4042, New: 4042, Python Garbage: 0, Python Collection Counts: (660, 5, 0) 
(1) This: 4061, New: 19, Python Garbage: 0, Python Collection Counts: (90, 6, 0) 
(2) This: 4064, New: 3, Python Garbage: 0, Python Collection Counts: (127, 6, 0) 
(3) This: 4067, New: 3, Python Garbage: 0, Python Collection Counts: (130, 6, 0) 
(4) This: 4070, New: 3, Python Garbage: 0, Python Collection Counts: (133, 6, 0) 
(5) This: 4073, New: 3, Python Garbage: 0, Python Collection Counts: (136, 6, 0) 
(6) This: 4076, New: 3, Python Garbage: 0, Python Collection Counts: (139, 6, 0) 
(7) This: 4079, New: 3, Python Garbage: 0, Python Collection Counts: (142, 6, 0) 
(8) This: 4082, New: 3, Python Garbage: 0, Python Collection Counts: (145, 6, 0) 
(9) This: 4085, New: 3, Python Garbage: 0, Python Collection Counts: (148, 6, 0) 

Każdy 100-ty iteracji, 100 obiekty są uwolnione, więc len(gc.get_objects() wzrost o 200 co 100 iteracji. len(gc.garbage) nigdy nie zmienia się od 0. Liczba kolekcji drugiej generacji rośnie powoli, a liczba 0 i 1 wzrasta.

+1

ten gromadzi się wiele nieodebranych przedmiotów. Jednak to nie trwa do zatrzymania, prawda? Czy potrafisz opracować podobny mały skrypt, który faktycznie przestawia się do zatrzymania? –

Odpowiedz

2

Nie mogę odtworzyć żadnego rzeczywistego wycieku w moim systemie, ale myślę, że twoje "co 100-ta iteracja, 100 obiektów jest wolnych" to trafienie w pamięć podręczną dla skompilowanych wyrażeń regularnych (przez moduł glob). Jeśli zajrzysz do re.py, domyślnie zobaczysz, że _MAXCACHE ma wartość 100, i domyślnie cała pamięć podręczna jest zszokowana po jej kliknięciu (w _compile). Jeśli zadzwonisz pod numer re.purge() przed rozmowami gc, prawdopodobnie odejdziesz.

(nota Ja tylko sugeruje re.purge() tutaj, aby sprawdzić, czy pamięć ma wpływ na wyniki GC. To nie powinno być konieczne, że w rzeczywistym kodzie).

wątpię, że naprawia swój ogromny wzrost pamięć problemu chociaż.

+0

Dzięki za to - kiedy zrobiłem to, co zasugerowałeś, efekt rzeczywiście zniknął, a nowe obiekty na pętlę zmieniły się na 2. Nie rozwiązuje problemu zwiększenia pamięci, ale z pewnością pomoże zrozumieć, co się dzieje. – Andy

6

Śledziłem to do modułu fnmatch. glob.glob wywołuje polecenie fnmatch, aby faktycznie wykonać globbing, a fnmatch ma pamięć podręczną wyrażeń regularnych, która nigdy nie jest czyszczona. W związku z tym pamięć podręczna stale powiększała się i nie była zaznaczona. Złożyłem błąd w bibliotece fnmatch [1].

[1]: http://bugs.python.org/issue7846 Pythona błędów

+0

Zastanawiam się, jak udało mi się wykryć podobny bufor w module re, ale nie ten! Być może powinienem odjąć punkt od mojej własnej odpowiedzi na to ... – mzz