2014-10-15 13 views
6

Ho un'utilità Python che va su un file tar.xz ed elabora ciascuno dei singoli file. Questo è un file compresso di 15 MB, con 740 MB di dati non compressi.Perdita di oggetti TarInfo

Su uno specifico server con memoria molto limitata, il programma si arresta in modo anomalo perché esaurisce la memoria. Ho usato objgraph per vedere quali oggetti sono stati creati. Si scopre che le istanze TarInfo non vengono rilasciate. Il ciclo principale è simile a questo:

with tarfile.open(...) as tar: 
    while True: 
     next = tar.next() 
     stream = tar.extractfile(next) 
     process_stream() 
     iter+=1 
     if not iter%1000: 
      objgraph.show_growth(limit=10) 

L'uscita è molto coerente:

TarInfo  2040  +1000 
TarInfo  3040  +1000 
TarInfo  4040  +1000 
TarInfo  5040  +1000 
TarInfo  6040  +1000 
TarInfo  7040  +1000 
TarInfo  8040  +1000 
TarInfo  9040  +1000 
TarInfo 10040  +1000 
TarInfo 11040  +1000 
TarInfo 12040  +1000 

questo va avanti fino a quando tutti i 30.000 file vengono elaborati.

Giusto per essere sicuro, ho commentato le linee di creazione del flusso e l'elaborazione. L'utilizzo della memoria è rimasto lo stesso: le istanze di TarInfo sono trapelate.

Sto usando Python 3.4.1 e questo comportamento è coerente su Ubuntu, OS X e Windows.

risposta

5

Sembra che questo sia in realtà di progettazione. L'oggetto TarFile mantiene un elenco di tutti gli oggetti TarInfo che contiene in un attributo members. Ogni volta che si chiama next, l'oggetto TarInfo estrae dall'archivio viene aggiunto alla lista:

def next(self): 
    """Return the next member of the archive as a TarInfo object, when 
     TarFile is opened for reading. Return None if there is no more 
     available. 
    """ 
    self._check("ra") 
    if self.firstmember is not None: 
     m = self.firstmember 
     self.firstmember = None 
     return m 

    # Read the next block. 
    self.fileobj.seek(self.offset) 
    tarinfo = None 
    ... <snip> 

    if tarinfo is not None: 
     self.members.append(tarinfo) # <-- the TarInfo instance is added to members 

La lista members sarà solo continuare a crescere come si estrae più elementi. Ciò consente l'utilizzo dei metodi getmembers e getmember, ma è solo una seccatura per il tuo caso d'uso. Sembra che la soluzione migliore è quella di tenere solo deselezionando l'attributo members come eseguire iterazioni (come suggerito here):

with tarfile.open(...) as tar: 
    while True: 
     next = tar.next() 
     stream = tar.extractfile(next) 
     process_stream() 
     iter+=1 
     tar.members = [] # Clear members list 
     if not iter%1000: 
      objgraph.show_growth(limit=10) 
+0

Wow, eccellente! Grazie! – zmbq