python解決循環(huán)引用的邏輯還是比較簡(jiǎn)單枣耀,今晚寫了一個(gè)腳本層的查找unreachable object的方法捞奕,模擬的就是查找循環(huán)引用的算法
# -*- coding:utf-8 -*-
import gc
import sys
REACHABLE = -10001
TENTATIVELY_UNREACHABLE = -10002
def get_unreachable_objects():
# 1. update refs
gc_refs = {}
for obj in gc.get_objects():
assert id(obj) not in gc_refs
# 在這個(gè)過(guò)程中會(huì)增加三個(gè)引用
# 1. 放入list一個(gè)引用
# 2. obj一個(gè)引用
# 3. 調(diào)用getrefcount(obj)會(huì)產(chǎn)生一個(gè)臨時(shí)引用
gc_refs[id(obj)] = sys.getrefcount(obj) - 3
obj = None
# 2. substract refs
for obj in gc.get_objects():
for child in gc.get_referents(obj):
if id(child) in gc_refs:
gc_refs[id(child)] -= 1
child = None
obj = None
# 3. get unreachable
is_running = True
while is_running:
for obj in gc.get_objects():
assert id(obj) in gc_refs
if gc_refs[id(obj)] == REACHABLE:
continue
if gc_refs[id(obj)] > 0:
gc_refs[id(obj)] = REACHABLE
for child in gc.get_referents(obj):
if id(child) in gc_refs:
if gc_refs[id(child)] == 0:
gc_refs[id(child)] = 1
elif gc_refs[id(child)] == TENTATIVELY_UNREACHABLE:
gc_refs[id(child)] = 1
else:
assert gc_refs[id(child)] > 0 or gc_refs[id(child)] == REACHABLE
child = None
elif gc_refs[id(obj)] == 0:
gc_refs[id(obj)] = TENTATIVELY_UNREACHABLE
else:
assert gc_refs[id(obj)] in (REACHABLE, TENTATIVELY_UNREACHABLE)
obj = None
is_running = False
for v in gc_refs.itervalues():
if v not in (REACHABLE, TENTATIVELY_UNREACHABLE):
is_running = True
break
v = None
unreachable = [k for k, v in gc_refs.iteritems() if v == TENTATIVELY_UNREACHABLE]
ret = []
for obj in gc.get_objects():
if id(obj) in unreachable:
ret.append(obj)
obj = None
return ret
if __name__ == "__main__":
class A(object): pass
a = A()
b = A()
c = A()
b.v = c
c.v = b
b_id = id(b)
a_id = id(c)
a = None
b = None
c = None
print get_unreachable_objects()
gc.set_debug(gc.DEBUG_LEAK | gc.DEBUG_STATS)
gc.collect()
腳本分為三步:
- update_refs
- substract refs
- find unreachable objects
與Python GC的邏輯基本一樣颅围,目前在我們的開(kāi)發(fā)服上測(cè)試還是有些問(wèn)題