如何在环状数据结构中管理内存
问题举例
在python中,垃圾回收器通过引用计数来回收垃圾对象,
在某些环状数据结构(树,图...),存在对象间的循环引用,比如树的父节点引用子节点,
子节点同时引用父节点。测试同时del掉引用父子节点,两个对象不能被立即回收。
分析
当一个对象引用计数为0,或者只剩下弱引用时,这个对象会被释放。
类中有一个内置方法__del__,这个方法会在类对象释放时调用。
来个栗子
classA:def __del__(self):print("__del__")
a1=A()
a2= a1;
说明:
(1)A创建创建的对象有两个计数引用(a1和a2)
(2)当我们通过python -i test.py加载并运行这个文件到交互解释器中,__del__方法并没有被调用
(3)但是如果我们把a1, a2都指向别的对象时,A创建的对象引用计数为0,这时对象就会被释放
如下
classA:def __del__(self):print("__del__")
a1=A()
a2=a1;
a1=None
a2= None
注意:
这个时候可能有的朋友发现通过python test.py运行第一段代码时,__del__方法也会被调用,这是为什么呢?
其实答案很简单,通过python test.py运行代码后相当于直接退出程序,当一个程序退出时,这个程序内的所有变量都会被释放。
弱引用
弱引用不增加引用计数,使用弱引用访问对象得到对象引用
importweakrefclassA:def __del__(self):print("__del__")
a1=A()
a2=weakref.ref(a1)
a3=a2()print(a3 is a1) #True
解决思路
使用标准库weakref.ref,它可以创建一种能访问对象但是不增加引用计数的对象
栗子
有一个双向链表,有3个节点:1, 2, 3,变量head指向节点1,节点1右引用节点2,节点2右引用节点3,
节点3左弱引用节点2,节点2左弱引用节点1,如图
说明:当变量head指向None时,节点1对象被释放,节点1的右引用节点2被释放,节点2的右引用节点3被释放
代码
importweakrefclassNode:def __init__(self, data):
self.data=data
self._left=None
self.right=Nonedefadd_right(self, node):
self.right=node
node._left=weakref.ref(self)
@propertydefleft(self):returnself._left()def __str__(self):return 'Node:' %self.datadef __del__(self):print('in __del__: delete %s' %self)defcreate_linklist(n):
head= current = Node(1)for i in range(2, n + 1):
node=Node(i)
current.add_right(node)
current=nodereturnhead
head= create_linklist(1000)print(head.right, head.right.left)
input()
head=Noneimporttimefor _ in range(1000):
time.sleep(1)print('run...')
input('wait...')