Python 的垃圾收集器不会清理具有“自定义”__del__
方法的循环依赖项。
由于您已经有了一个__del__
方法,您只需要一个循环依赖来“禁用”这些对象的 GC:
class Handle(object):
def __init__(self):
self.handle = do_stuff("get")
self._self = self
现在,这会造成内存泄漏,那么我们该如何解决呢?
准备好释放对象后,只需删除循环依赖项:
import threading
import gc
class Handle(object):
def __init__(self):
self.handle = do_stuff("get")
self._self = self
def close(self):
h = self.handle
self.handle = None
if h is not None:
do_stuff("close %d" % h)
def __del__(self):
self.close()
_resource_lock = threading.Lock()
def do_stuff(what):
_resource_lock.acquire()
try:
# GC can be invoked here -> deadlock!
for j in range(20):
list()
return 1234
finally:
_resource_lock.release()
for j in range(1000):
xs = []
b = Handle()
xs.append(b)
xs.append(xs)
# Make sure the GC is up to date
gc.collect()
print "Length after work", len(gc.garbage)
# These are kept along due to our circular depency
# If we remove them from garbage, they come back
del gc.garbage[:]
gc.collect()
print "Length now", len(gc.garbage)
# Let's break it
for handle in gc.garbage:
handle._self = None
# Now, our objects don't come back
del gc.garbage[:]
gc.collect()
print "Length after breaking circular dependencies", len(gc.garbage)
会做:
Length after work 999
Length now 999
Length after breaking circular dependencies 0
另一方面,为什么您需要在清理代码中访问这个复杂的库,而您无法控制其执行?
这里一个更干净的解决方案可能是在循环中进行清理,并在清理后打破循环依赖,以便 GC 可以做它的事情。
这是一个实现:
import threading
import gc
class Handle(object):
def __init__(self):
self.handle = do_stuff("get")
self._self = self
def close(self):
h = self.handle
self.handle = None
if h is not None:
do_stuff("close %d" % h)
del self._self
def __del__(self):
# DO NOT TOUCH THIS
self._ = None
_resource_lock = threading.Lock()
def do_stuff(what):
_resource_lock.acquire()
try:
# GC can be invoked here -> deadlock!
for j in range(20):
list()
return 1234
finally:
_resource_lock.release()
for j in range(1000):
xs = []
b = Handle()
xs.append(b)
xs.append(xs)
# Make sure the GC is up to date
gc.collect()
print "Length after work", len(gc.garbage)
# These are kept along due to our circular depency
# If we remove them from garbage, they come back
del gc.garbage[:]
gc.collect()
print "Length now", len(gc.garbage)
# Let's break it
for handle in gc.garbage:
handle.close()
# Now, our objects don't come back
del gc.garbage[:]
gc.collect()
print "Length after breaking circular dependencies", len(gc.garbage)
输出显示我们的循环依赖确实阻止了收集:
Length after work 999
Length now 999
Length after breaking circular dependencies 0