【python】垃圾回收机制:示例(二)

上文讲解了垃圾回收机制的原理,本文将通过示例来分析引用计数和循环引用。

目录

一、引用计数

1、示例

2、说明

3、强制清除引用

二、循环引用

1、示例

2、说明


一、引用计数

上文已经提过,当引用计数达到0时,该对象被视为不再被使用,其占用的内存空间就会被回收。

1、示例

import sys  
  
class MyClass:  
    def __init__(self, name):  
        self.name = name  
  
def main():  
    # 创建一个对象,此时它的引用计数为 1  
    obj = MyClass("example")  
    print(sys.getrefcount(obj))  # 通常会打印出 2,因为 getrefcount 函数本身也会增加一次引用  
  
    # 创建一个新的引用到同一个对象,引用计数增加  
    ref = obj  
    print(sys.getrefcount(obj))  # 通常会打印出 3  
  
    # 删除一个引用,引用计数减少  
    del ref  
    print(sys.getrefcount(obj))  # 通常会打印出 2  
  
    # 当 obj 超出作用域(即 main 函数结束)时,它的引用计数会变为 0,对象会被回收  
  
if __name__ == "__main__":  
    main()

2、说明

  1. sys.getrefcount(obj) 函数返回的是对象 obj 的引用计数,但需要注意的是,由于 getrefcount 函数本身也会创建一个对 obj 的临时引用,所以实际打印的引用计数可能会比你预期的多一个。
  2. 当对象超出其作用域时(例如,函数返回后,函数内的局部变量就会被回收),对象的引用计数会减少。如果此时引用计数变为 0,对象就会被垃圾回收器回收。2、函数局部变量回收

3、强制清除引用

在某些特定情况下,想要“释放”或“减少”Python 对象占用的内存。虽然不能直接“释放”一个对象的内存(就像 C 或 C++ ),但可以通过以下方式来影响 Python 的内存使用:

  • 删除对象引用

    当不需要某个对象时,可以删除所有指向它的引用。这样,Python 的垃圾回收器就会在适当的时候回收该对象占用的内存。

    obj = [1, 2, 3, 4]  # 创建一个列表对象  
    del obj             # 删除对列表的引用,当没有其他引用时,列表对象可能会被回收
  • 使用 gc 模块

     Python 提供了 gc 模块,它允许我们与 Python 的垃圾回收器进行交互。可以使用 gc.collect() 来强制进行垃圾回收,但是一般不需要手动执行,因为 Python 的垃圾回收器会自动运行。

import gc  
# ... 创建和删除一些对象 ...  
gc.collect()  # 强制进行垃圾回收

二、循环引用

两个对象互相引用,但是没有其他对象引用了,但是引用计数不为0,也不会被垃圾回收清空。

1、示例

import gc
import os
import psutil
import time


def create_cyclic_references():
    # 获取当前进程ID
    pid = os.getpid()

    # 获取创建循环引用前的内存使用情况
    process = psutil.Process(pid)
    memory_before = process.memory_info().rss / (1024 ** 3)  # 转换为GB
    print(f"Memory usage before creating cyclic references: {memory_before:.2f} GB")

    # 初始化两个列表并创建循环引用
    a = [i for i in range(10000000)]
    b = [i for i in range(10000000)]
    a.append(b)
    b.append(a)

    # 等待一段时间以便垃圾回收器有机会运行(尽管循环引用不会被回收)
    time.sleep(1)

    # 获取创建循环引用后的内存使用情况
    memory_after = process.memory_info().rss / (1024 ** 3)  # 转换为GB
    print(f"Memory usage after creating cyclic references: {memory_after:.2f} GB")


# 调用函数以创建循环引用并查看内存差异
create_cyclic_references()
# 注意:由于循环引用,Python的垃圾回收器不会回收a和b,
# 但在函数外部我们打印了函数返回的final_memory_usage
# 等待一段时间以便垃圾回收器有机会运行(尽管循环引用不会被回收)
time.sleep(1)
# 获取函数调用结束后的内存使用情况
pid = os.getpid()
process = psutil.Process(pid)
final_memory_usage = process.memory_info().rss / (1024 ** 3)  # 转换为GB
print(f"Memory usage after function call: {final_memory_usage:.2f} GB")
gc.collect() # 强制回收
final_memory_usage = process.memory_info().rss / (1024 ** 3)  # 转换为GB
print(f"Memory usage after gc.collect(): {final_memory_usage:.2f} GB")

2、说明

刚调用函数是0.01g,初始化数据后是0.39g,函数调用结束还是0.39g。但是函数调用已经结束,参数a和b应该被回收才对,但是因为互相引用,导致引用数不为0,无法被回收。最后强制回收之后,内存变回了0.01g。

在这里为了演示采用了强制回收,但是实际上python使用了标记-清除算法分代收集算法来解决这种情况。

标记-清除算法分代收集算法的原理可以看上一篇文章。

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值