2010-02-02 4 views
12

Ich schreibe einen benutzerdefinierten Dateisystem-Crawler, der Millionen von Globs zur Verarbeitung über sys.stdin übergeben wird. Ich stelle fest, dass bei der Ausführung des Skripts die Speichernutzung im Laufe der Zeit massiv ansteigt und das Ganze praktisch zum Stillstand kommt. Ich habe unten einen minimalen Fall geschrieben, der das Problem zeigt. Mache ich etwas falsch oder habe ich einen Fehler in Python/dem Glob-Modul gefunden? (Ich benutze Python 2.5.2).Warum lecke ich Speicher mit dieser Python-Schleife?


#!/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 

Die Ausgabe sieht so aus:

 
(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) 

Jede 100. Iteration 100 Objekte freigegeben werden, so len(gc.get_objects() erhöht sich um 200 alle 100 Iterationen. len(gc.garbage) wechselt nie von 0. Die Zählrate der zweiten Generation steigt langsam an, während die 0. und 1. Zählung auf und ab geht.

+1

Diese viel uncollected Objekte ansammelt. Das hält jedoch nicht an, oder? Können Sie ein ähnliches kleines Skript aufarbeiten, das tatsächlich zum Stillstand kommt? –

Antwort

2

Ich kann kein tatsächliches Leck auf meinem System reproduzieren, aber ich denke, Ihre "100. Iteration, 100 Objekte sind freigegeben" trifft Sie den Cache für kompilierte reguläre Ausdrücke (über das Glob-Modul). Wenn Sie auf re.py schauen, sehen Sie _MAXCACHE standardmäßig auf 100, und standardmäßig wird der gesamte Cache weggeblasen, sobald Sie diesen gefunden haben (in _compile). Wenn Sie re.purge() vor Ihren gc Anrufen anrufen, werden Sie wahrscheinlich sehen, dass dieser Effekt weggeht.

(Anmerkung Ich schlage nur re.purge() hier, dass die Cache-Speicher zu überprüfen, wird Ihre gc Ergebnisse zu beeinflussen. Es ist nicht notwendig sein soll, dass in Ihrem eigentlichen Code zu haben.)

Ich bezweifle, dass Ihr massives Speicher Anstieg Problem behebt obwohl.

+0

Danke dafür - als ich das tat, was Sie vorgeschlagen haben, ging der Effekt tatsächlich verloren, und die neuen Objekte pro Schleife änderten sich auf 2. Es behebt nicht das Speichererhöhungsproblem, aber es wird sicherlich helfen zu verstehen, was vor sich geht. – Andy

6

Ich verfolgte dies bis zum fnmatch-Modul. glob.glob ruft fnmatch auf, um das Globbing auszuführen, und fnmatch hat einen Cache regulärer Ausdrücke, der niemals gelöscht wird. Bei dieser Verwendung wurde der Cache kontinuierlich und unkontrolliert erweitert. Ich habe einen Fehler gegen die fnmatch-Bibliothek [1] eingereicht.

[1]: http://bugs.python.org/issue7846 Python Bug

+0

Ich frage mich, wie ich es geschafft habe, den ähnlichen Cache im Modul zu finden, aber nicht diesen! Vielleicht sollte ich einen Punkt von meiner eigenen Antwort dafür abziehen ... – mzz