use*_*523 5 python garbage-collection
我正在使用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)
Run Code Online (Sandbox Code Playgroud)
这会返回:
1. Nbr of instance of A in gc :
1
2. Nbr of instance of A in gc :
0
Run Code Online (Sandbox Code Playgroud)
我在等两个人都有0.A实例存储在哪里?
非常感谢Alex
该实例存储(至少)存储在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)]
Run Code Online (Sandbox Code Playgroud)
这打印:
<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 :
[]
Run Code Online (Sandbox Code Playgroud)
注意最后一个对象与框架相同raiser.这也意味着如果你只是写,你也会得到相同的结果
try:
A().raiser()
except:
pass
Run Code Online (Sandbox Code Playgroud)
我们可以再次做同样的技巧来看看持有框架对象的是什么:
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)]
Run Code Online (Sandbox Code Playgroud)
结果是
[(['<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'>])]
Run Code Online (Sandbox Code Playgroud)
所以我们看到框架至少由一个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
Run Code Online (Sandbox Code Playgroud)
在代码中添加一些调试行:
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)
Run Code Online (Sandbox Code Playgroud)
回报
1. Tracking a: True
2. Tracking a: False
Run Code Online (Sandbox Code Playgroud)
这表明该对象在之后没有被跟踪,a = None因此当需要空间时它将从堆中释放。soa没有被存储,但 Python 认为没有必要完全取消引用它(忽略它可能比从堆栈中清除它更便宜)。
然而,使用 python 来解决性能敏感的问题是一个坏主意——二进制文件本身很大,而且有很多你永远不需要的包带来的膨胀。为什么不试试把你的手变成小C呢?