打破链表对Python的垃圾收集器有帮助吗?



我想知道——当我有机会——当我不再需要Python中的单连接链表时,我是否应该分解它,或者我是否不应该打扰自己。

列表的例子:

class Link:
def __init__(self, next=None):
self.next = next

L = Link(Link(Link(Link())))

断开链接:

def break_(link):
if link is not None:
break_(link.next)
link.next = None  # link broken
# application
break_(L)

树的例子:

class Node:
def __init__(self, l=None, r=None):
self.l = l
self.r = r
T = 
Node(
Node(
Node(), 
Node()
),
Node(
Node(), 
Node()
),
)

断开链接:

def break_(node):
if node is not None:
break_(node.l)
break_(node.r)
node.l = None  # link broken
node.r = None  # link broken

# application
break_(T)

基本上,我想知道的是,在这种情况下编写代码的性能最佳方法是什么。GC准备好处理大型链接结构了吗?难道它不需要运行可能很长的带有计数器之类的dfse来确定哪些对象可以被释放吗?断开链接并给GC一堆在任何地方都没有引用的松散对象不是更简单吗?我可以对此进行基准测试,但我正在寻找一个解释(最好是来自Karl),如果有的话。谢谢你。

您可以在类中添加__del__方法,以查看对象何时即将被清理:

class Node:
def __init__(self, name, l=None, r=None):
self.name = name
self.l = l
self.r = r
def __del__(self):
print(f'__del__ {self.name}')

T = 
Node('0',
Node('0L',
Node('0LL'), 
Node('0LR'),
),
Node('0R',
Node('0RL'), 
Node('0RR'),
),
)
del T  # decreases the reference count of T by 1

对于del T,T所引用的对象的引用计数减少了1。在这种情况下,引用计数恰好达到0。CPython知道这一点,因为它将引用计数与每个对象一起存储。当一个对象的引用计数达到0时,该对象将被标记为清理。这种清理在控制权交还给用户代码之前进行。因此,对于上面的示例,这意味着T最初引用的对象立即被清理。这首先调用__del__,然后调用相应的c代码进行清理。这反过来遍历对象的实例字典,并将存储在那里的其他对象的引用计数减少1。如果另一个对象的引用计数在该进程中达到0,它也被标记为清理。然后重复此过程,直到没有对象被标记为需要清理,并且将控制权交还给主循环。

这是示例的输出:

__del__ 0
__del__ 0L
__del__ 0LL
__del__ 0LR
__del__ 0R
__del__ 0RL
__del__ 0RR

如上所述,当0被清理时,它引用的所有其他对象的引用计数减少1(即0L0R),如果引用计数达到0,也会被清理。

如果您通过将相应的实例属性设置为None来手动中断列表的链接,这会增加额外的开销,因为它必须实际修改内存中的所有这些对象(更新它们的实例字典)。子对象达到0的引用计数在这里更像是一个副产品。还要注意,由于clean函数优先执行深度,因此清理顺序发生了变化:

def clean(node):
if node is not None:
clean(node.l)
clean(node.r)
node.l = None  # this updates the object in memory
node.r = None
clean(T)

生产

__del__ 0LL
__del__ 0LR
__del__ 0RL
__del__ 0RR
__del__ 0L
__del__ 0R
__del__ 0

相关内容

  • 没有找到相关文章

最新更新