36. 在环状数据结构中管理内存

在Python中,垃圾回收器通过引用计数来回收垃圾对象。但某些环状数据结果(树、图、双向链表等),存在对象间的循环引用,比如树的父节点引用子节点,子节点同时也引用父节点。如果同时del掉引用父子节点,两个对象不能被立即回收。

要求:解决此类的内存管理问题。

解决方案:

使用标准库weakref.ref类,它可以创建一种能访问对象但不增加引用计数的对象。


  • 对于wekref模块:
class weakref.ref(object[, callback])

返回对对象的弱引用。如果原始对象仍然存活,则可以通过调用引用对象来检索原始对象;如果引用的原始对象不再存在,则调用引用对象将得到None。

Python 的弱引用模块weakref允许垃圾回收器(garbage collector)在一个对象没有强引用的时候回收内存。

对一个对象的弱引用,相对于通常的引用来说,如果一个对象有一个常规的引用,它是不会被垃圾回收器销毁的,但是如果一个对象只剩下一个弱引用,那么它可能被垃圾回收器收回。

  • 对于引用计数:

要保持追踪内存中的对象,Python使用了引用计数这一简单的技术,当一个对象的引用计数为0时该对象会被释放,此时会调用析构函数。如果要显式的调用析构函数,可以使用del关键字。

sys.getrefcount(a)可以查看a对象的引用计数,但是比正常计数大1,因为调用函数的时候传入a,这会让a的引用计数+1。

>>> class A:
	def __del__(self):              #析构函数
		print('in __del__')

		
>>> a = A()

>>> import sys

>>> sys.getrefcount(a)              #a的引用计数为1
2

>>> a2 = a

>>> sys.getrefcount(a)              #a的引用计数为2
3

>>> a2 = None

>>> a = 1
in __del__              #此时直接调用析构函数,说明引用计数已经为0

  • 解决方案:
import time

class Node:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None

    def add_right(self, node):
        self.right = node
        node.left = self

    def __str__(self):              #打印node
        return 'None:<%s>' % self.data

    def __del__(self):              #查看释放过程
        print('in __del__: delete %s' % self)


def create_linklist(n):
    head = current = Node(1)
    for i in range(2, n+1):
        node = Node(i)
        current.add_right(node)
        current = node
    return head

head = create_linklist(1000)
head = None             #令head引用计数减1

for _ in range(1000):               #模拟程序运行
    time.sleep(1)
    print('run...')
input('wait...')

此时在释放双向链表的head(头节点)时,内存一直无法释放,因为头节点的引用计数不为0,这就导致后面的节点的引用计数也不为0。

改进:使用弱引用,通过标准库weakref.ref类,创建一种能访问对象但不增加引用计数的对象。

在这里插入图片描述

import time

class Node:
    def __init__(self, data):
        self.data = data
        self._left = None
        self.right = None

    def add_right(self, node):
        self.right = node
        node._left = weakref.ref(self)              #创建弱引用

    @property
    def left(self):
        return self._left

    def __str__(self):
        return 'None:<%s>' % self.data

    def __del__(self):
        print('in __del__: delete %s' % self)


def create_linklist(n):
    head = current = Node(1)
    for i in range(2, n+1):
        node = Node(i)
        current.add_right(node)
        current = node
    return head

head = create_linklist(1000)
head = None             #令head引用计数减1

for _ in range(1000):               #模拟程序运行
    time.sleep(1)
    print('run...')
input('wait...')

此时运行程序,可以看到析构函数的调用,即内存立即释放。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值