5 Stimmen

Objekt wird nach einer Ausnahmeerhöhung in Python 2.7 nicht freigegeben

Ich verwende Python 2.7 und versuche, einen sauberen Speicher zu haben (da ich einen kleinen Server schreibe). Ich stehe vor einem Problem, wo das Objekt der letzten Erhöhung immer noch im Garbage Collector gehalten werden (und dann __ del __ nicht nach dem ersten Try / Except aufgerufen wird).

Hier ist ein kleines Beispiel:

import gc

class A(object):
    def raiser(self):
        0/0 # will raise an exception
a = A()

try:
    a.raiser()
except:
    pass

a = None # should release the object from memory

gc.collect() # just to be sure, but doesn't do anything

print '1. Nbr of instance of A in gc : '
print len([o for o in gc.get_objects() if isinstance(o, A)]) # get 1 but expected 1

try:
    0/0
except:
    pass
print '2. Nbr of instance of A in gc : '
print len([o for o in gc.get_objects() if isinstance(o, A)]) # get 0 (finally)

und das kommt zurück:

1. Nbr of instance of A in gc : 
1
2. Nbr of instance of A in gc : 
0

während ich darauf wartete, 0 für beide zu haben. Wo ist die A-Instanz gespeichert?

Herzlichen Dank! Alex

7voto

kennytm Punkte 488916

Diese Instanz wird (zumindest) in dem unterbrochenen Rahmen des raiser Funktion, wie wir mit gc.get_referrers :

import gc
import inspect

class A(object):
    def raiser(self):
        print inspect.currentframe()
        0/0
a = A()

try:
    a.raiser()
except:
    pass

a = None # should release the object from memory
gc.collect() # just to be sure, but doesn't do anything

print 'b. Nbr of instance of A in gc : '
print [map(lambda s: str(s)[:64], gc.get_referrers(o)) for o in gc.get_objects() if isinstance(o, A)]

try:
    0/0
except:
    pass

print '---'

print 'c. Nbr of instance of A in gc : '
print [map(lambda s: str(s)[:64], gc.get_referrers(o)) for o in gc.get_objects() if isinstance(o, A)]

Dies wird gedruckt:

<frame object at 0x239fa70>
---
b. Nbr of instance of A in gc : 
[["[[], ('Return a new Arguments object replacing specified fields ",
  "{'A': <class '__main__.A'>, 'a': None, '__builtins__': <module '",
  '<frame object at 0x239fa70>']]
---
c. Nbr of instance of A in gc : 
[]

Beachten Sie, dass das letzte Objekt dasselbe ist wie der Rahmen von raiser . Das bedeutet auch, dass Sie das gleiche Ergebnis erhalten, wenn Sie einfach schreiben

try:
    A().raiser()
except:
    pass

Wir könnten den gleichen Trick noch einmal anwenden, um zu sehen, was das Rahmenobjekt hält:

class A(object):
    def raiser(self):
        0/0

try:
    A().raiser()
except:
    pass

print [(map(lambda x: str(x)[:64], gc.get_referrers(o)),  # Print the referrers
        map(type, gc.get_referents(o)))  # Check if it's the frame holding an A
           for o in gc.get_objects()
           if inspect.isframe(o)]

Das Ergebnis ist

[(['<traceback object at 0x7f07774a3bd8>',
   '[<function getblock at 0x7f0777462cf8>, <function getsourcelines',
   "{'A': <class '__main__.A'>, '__builtins__': <module '__builtin__"
  ], [<type 'frame'>, <type 'code'>, <type 'dict'>, <type 'dict'>,
      <class '__main__.A'>]),
 (['<frame object at 0xe3d3c0>',
   '<traceback object at 0x7f07774a3f38>',
   '[<function getblock at 0x7f0777462cf8>, <function getsourcelines',
   "{'A': <class '__main__.A'>, '__builtins__': <module '__builtin__"
  ], [<type 'code'>, <type 'dict'>, <type 'dict'>, <type 'dict'>,
      <type 'NoneType'>])]

Wir sehen also, dass der Rahmen zumindest von einem traceback Objekt. Wir können Informationen über das traceback Objekt in der traceback Modul, in dem erwähnt wird:

Das Modul verwendet Traceback-Objekte - dies ist der Objekttyp, der in den Variablen gespeichert wird sys.exc_traceback (veraltet) und sys.last_traceback und wird als drittes Element von sys.exc_info() .

Das bedeutet, dass diese Systemvariablen diejenige sein können, die den Rahmen am Leben hält. In der Tat, wenn wir sys.exc_clear() um die Ausnahmeinformation zu löschen, wird die Instanz deallokiert:

import gc
import sys

class A(object):
    def raiser(self):
        0/0

try:
    A().raiser()
except:
    pass

print len([o for o in gc.get_objects() if isinstance(o, A)])  # prints 1
sys.exc_clear()
print len([o for o in gc.get_objects() if isinstance(o, A)])  # prints 0

4voto

soulcheck Punkte 35658

Nach einer kleinen Untersuchung. Wenn Sie importieren sys und setzen

...
a = None

print sys.exc_info()

#sys.exc_clear() # if you add this your A instance will get gc as expected 

gc.collect()
...

werden Sie feststellen, dass der Interpreter immer noch einen Verweis auf die ZeroDivisionError nach innen gewölbt try...catch auch wenn der Code bereits draußen ist. Da Ausnahme halten Verweise auf Frames, wo sie ausgelöst werden (wenn auch nur, um Traceback zu drucken), Ihre A-Instanz hat noch nicht Null refcount.

Sobald eine weitere Ausnahme ausgelöst (und behandelt) wird, lässt der Interpreter den Verweis auf die erste Ausnahme fallen und sammelt sowohl die Ausnahme als auch die mit ihr verbundenen Objekte ein.

1voto

danodonovan Punkte 18292

Hinzufügen einiger Debug-Zeilen zu Ihrem Code:

import gc
gc.set_debug(gc.DEBUG_STATS)

class A(object):
    def raiser(self):
        0/0 # will raise an exception
a = A()

print 'Tracking a:', gc.is_tracked(a)

try:
    a.raiser()
except:
    pass

a = None # should release the object from memory

print 'Tracking a:', gc.is_tracked(a)

gibt zurück.

1. Tracking a: True
2. Tracking a: False

Dies zeigt, dass das Objekt nicht mehr verfolgt wird, nachdem die a = None so dass er aus dem Heap freigegeben wird, wenn der Platz benötigt wird. Also a es ノット gespeichert, aber Python sieht nicht die Notwendigkeit, sie vollständig zu de-referenzieren (es ist wahrscheinlich billiger, sie zu ignorieren, als sie aus dem Stack zu löschen).

Die Verwendung von Python für leistungsabhängige Probleme ist jedoch eine schlecht Idee - die Binärdatei selbst ist groß, und es gibt eine Menge Blähungen durch Pakete, die Sie nie brauchen werden. Warum versuchen Sie nicht, ein wenig C zu schreiben?

1voto

user1143523 Punkte 79

Wenn das Objekt jemals eine lokale Variable in einer Funktion war, die einen Ausdruck in einer Ausnahmeklausel abgefangen hat, besteht die Wahrscheinlichkeit, dass ein Verweis auf das Objekt noch im Stackframe dieser Funktion vorhanden ist, wie in der Stacktrace enthalten. Normalerweise wird dies durch den Aufruf von sys.exc_clear() erledigt, indem die letzte aufgezeichnete Ausnahme gelöscht wird.

http://docs.python.org/faq/programming.html#id55

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X