Python 2.7 中引发异常后未释放对象
我正在使用 Python 2.7 并试图拥有干净的内存(因为我正在编写一个小型服务器)。我面临的问题是最后一次引发的对象仍然保留在垃圾收集器中(然后在第一次尝试/例外之后不会调用 __ del __ )。
这是一个小例子:
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)
它会返回:
1. Nbr of instance of A in gc :
1
2. Nbr of instance of A in gc :
0
当我等待两者都为 0 时, 。 A实例存储在哪里?
多谢 亚历克斯
I am using Python 2.7 and trying to have a clean memory (as I am writing a small server). I am facing an issue where the object of the last raise are still kept in the garbage collector (and then __ del __ is not called after the first try/except).
Here is a small example:
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)
and this returns:
1. Nbr of instance of A in gc :
1
2. Nbr of instance of A in gc :
0
while I was waiting to have 0 for both. Where Is the A instance stored ?
Thanks a lot
Alex
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
该实例(至少)存储在 raiser 函数的中断帧中,我们可以使用 gc.get_referrers 进行检查:
这将打印:
注意最后一个对象是相同的作为
raiser
的框架。 你也会得到相同的结果这也意味着如果你只写我们可以再次执行相同的技巧来查看保存框架对象的内容,
:结果是
所以我们看到框架至少由回溯保存对象。我们可以在
tracebacktraceback
对象的信息code> 模块,其中提到:这意味着这些系统变量可能是使帧保持活动状态的变量。事实上,如果我们调用
sys.exc_clear()
清除异常信息,实例将被释放:That instance is stored (at least) in the interrupted frame of the
raiser
function, as we can check usinggc.get_referrers
:This prints:
Note the last object is the same as the frame of
raiser
. This also means you'll get the same result too if you just writeWe could do the same trick again to see what is holding the frame object:
The result is
So we see that the frame is at least held by a
traceback
object. We can find info about thetraceback
object in thetraceback
module, which mentions:Which means these sys variables may be the one that is holding the frame alive. Indeed, if we call
sys.exc_clear()
to clear the exception info, the instance will be deallocated:经过一番调查。如果导入
sys
并放置,您会注意到解释器仍然保留对
try...catch
内部的ZeroDivisionError
的引用,即使代码已经在外面了。由于异常保留对抛出的帧的引用(如果只是为了打印回溯),因此您的 A 实例仍然具有非零引用计数。一旦抛出(并处理)另一个异常,解释器就会删除对第一个异常的引用,并收集异常和与其连接的对象。
After a little investigation. If you import
sys
and putyou'll notice that the interpreter still holds a reference to the
ZeroDivisionError
trown insidetry...catch
even though the code is already outside. Since exception hold references to frames where they are thrown (if only to print traceback), your A instance still has non-zero refcount.Once another exception is thrown (and handled) interpreter drops the reference to the first one and collects both the exception and objects connected to it.
在代码中添加一些调试行:
returns
这表明在
a = None
之后该对象没有被跟踪,因此当需要空间时它将从堆中释放。所以a
没有被存储,但是Python认为没有必要完全取消引用它(忽略它可能比从堆栈中清除它更便宜) 。然而,使用 python 来解决性能敏感问题是一个坏的主意 - 二进制文件本身很大,并且有很多来自你永远不需要的包的膨胀。为什么不试试把你的手变成小C呢?
Adding some debug lines to your code:
returns
This shows that the object is not being tracked after the
a = None
so it will be released from the heap when the space is needed. Soa
is not stored, but Python doesn't see the need to completely de-reference it (it is probably cheaper to ignore it than to purge it from the stack).However, using python for performance sensitive problems is a bad idea - the binary itself is large, and there is a lot of bloat from packages you will never need. Why not try turning your hand to a little C?
如果该对象曾经是捕获 except 子句中的表达式的函数中的局部变量,则对该对象的引用很可能仍然存在于该函数的堆栈帧中(如堆栈跟踪中所包含)。通常,调用 sys.exc_clear() 将通过清除最后记录的异常来解决此问题。
http://docs.python.org/faq/programming.html#id55
If the object has ever been a local variable in a function that caught an expression in an except clause, chances are that a reference to the object still exists in that function’s stack frame as contained in the stack trace. Normally, calling sys.exc_clear() will take care of this by clearing the last recorded exception.
http://docs.python.org/faq/programming.html#id55