Этот экземпляр сохраняется (по крайней мере) в прерванном кадре функции raiser
, как мы можем проверить, используя 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)]
Это печатает:
<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 :
[]
Обратите внимание, что последний объект такой же, как кадр raiser
. Это также означает, что вы получите тот же результат, если просто напишите
try:
A().raiser()
except:
pass
Мы могли бы снова сделать тот же трюк, чтобы увидеть, что держит объект рамки:
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)]
Результат
[(['<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'>])]
Итак, мы видим, что кадр по крайней мере удерживается объектом traceback
. Мы можем найти информацию об объекте traceback
в модуле traceback
, в котором упоминается:
Модуль использует объекты трассировки - это тип объекта, который хранится в переменных sys.exc_traceback
(устарело) и sys.last_traceback
и возвращается как третий элемент из sys.exc_info()
.
Это означает, что эти переменные sys могут быть теми, которые удерживают фрейм живым. Действительно, если мы вызовем sys.exc_clear()
для очистки информации об исключении, экземпляр будет освобожден:
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