Python垃圾回收机制详解

Python的垃圾回收机制

Python使用自动内存管理 ,主要通过引用计数分代回收两种机制来实现垃圾回收。

1. 引用计数(Reference Counting)

基本概念

  • 每个Python对象都有一个引用计数,表示有多少引用指向该对象
  • 当引用计数为0时,对象会被立即回收

引用计数变化规则:

python 复制代码
import sys

# 创建对象,引用计数为1
a = [1, 2, 3]  # 引用计数 = 1
print(f"初始引用计数: {sys.getrefcount(a)}")  # 注意:getrefcount()会增加临时引用

b = a  # 引用计数 +1,变为2
print(f"赋值后引用计数: {sys.getrefcount(a)}")

c = [a, a]  # 列表中有两个引用,引用计数 +2,变为4
print(f"放入容器后引用计数: {sys.getrefcount(a)}")

# 删除引用
del b  # 引用计数 -1,变为3
del c[0]  # 引用计数 -1,变为2
del c  # 删除整个列表,引用计数 -2(因为列表中有a的引用),变为0,对象被回收

引用计数的优缺点:

优点:

  • 实时性高:对象不再被引用时立即释放
  • 简单高效:大多数情况下开销小

缺点:

  • 循环引用问题:两个或多个对象相互引用,导致引用计数永不为0
  • 额外的内存开销:需要存储引用计数

2. 分代回收(Generational GC)

解决循环引用问题

分代回收是引用计数的补充,专门处理循环引用。

分代原理:

  • 将对象分为三代:0代、1代、2代
  • 新创建的对象进入0代
  • 对象存活时间越长,代数越高
  • 回收频率:0代 > 1代 > 2代

工作机制:

python 复制代码
import gc

# 创建循环引用示例
def create_cycle():
    a = [1, 2, 3]  # 对象A
    b = [4, 5, 6]  # 对象B
    a.append(b)    # A引用B
    b.append(a)    # B引用A,形成循环引用
    # 离开函数后,a和b的局部引用消失,但对象间仍相互引用
    # 引用计数不会为0,需要分代回收处理

# 启用/禁用垃圾回收
gc.enable()  # 默认启用
gc.disable()  # 临时禁用
gc.enable()

# 手动触发垃圾回收
collected = gc.collect()  # 返回回收的对象数量
print(f"回收了 {collected} 个对象")

# 获取分代回收统计信息
print(gc.get_stats())

分代回收算法:

python 复制代码
import gc

# 查看和设置各代阈值
print(f"GC阈值: {gc.get_threshold()}")
# 输出类似 (700, 10, 10)
# 第一个值:0代触发回收的阈值(分配的对象数 - 释放的对象数)
# 第二个值:0代回收次数达到该值时触发1代回收
# 第三个值:1代回收次数达到该值时触发2代回收

# 设置自定义阈值
gc.set_threshold(800, 10, 10)

# 获取各代对象数量
print(f"各代对象数量: {gc.get_count()}")

3. 完整示例

python 复制代码
import gc
import sys

class Node:
    def __init__(self, name):
        self.name = name
        self.next = None
        print(f"创建节点: {self.name}")
    
    def __del__(self):
        print(f"销毁节点: {self.name}")

# 演示引用计数
def demo_reference_counting():
    print("\n=== 引用计数演示 ===")
    obj1 = Node("对象1")  # 引用计数 = 1
    obj2 = obj1          # 引用计数 = 2
    print(f"引用计数: {sys.getrefcount(obj1)-1}")
    
    del obj2            # 引用计数 = 1
    del obj1            # 引用计数 = 0,立即销毁

# 演示循环引用
def demo_circular_reference():
    print("\n=== 循环引用演示 ===")
    gc.disable()  # 暂时禁用分代回收
    
    a = Node("A")
    b = Node("B")
    a.next = b  # A引用B
    b.next = a  # B引用A,形成循环引用
    
    # 删除外部引用
    del a
    del b
    
    # 此时两个对象相互引用,引用计数各为1
    # 但由于分代回收被禁用,它们不会被自动回收
    
    gc.enable()  # 重新启用分代回收
    collected = gc.collect()  # 手动触发回收
    print(f"回收了 {collected} 个对象")

# 演示分代回收
def demo_generational_gc():
    print("\n=== 分代回收演示 ===")
    
    # 创建大量对象
    objects = []
    for i in range(1000):
        obj = Node(f"临时对象{i}")
        objects.append(obj)
    
    # 查看GC统计
    print(f"GC计数: {gc.get_count()}")
    
    # 删除引用
    del objects
    
    # 触发垃圾回收
    collected = gc.collect()
    print(f"回收了 {collected} 个对象")

# 演示弱引用(避免循环引用)
import weakref

def demo_weak_reference():
    print("\n=== 弱引用演示 ===")
    
    class Data:
        def __init__(self, value):
            self.value = value
    
    obj = Data(100)
    weak_ref = weakref.ref(obj)  # 创建弱引用
    
    print(f"通过弱引用访问: {weak_ref()}")
    print(f"弱引用指向的对象: {weak_ref().value}")
    
    del obj  # 删除原始对象
    
    print(f"对象删除后弱引用: {weak_ref()}")  # 返回None

if __name__ == "__main__":
    demo_reference_counting()
    demo_circular_reference()
    demo_generational_gc()
    demo_weak_reference()

4. 实际应用建议

  1. 避免循环引用 :使用弱引用(weakref模块)或重新设计数据结构
  2. 及时释放大对象 :使用del显式删除不再需要的大对象
  3. 谨慎使用__del____del__方法可能影响垃圾回收
  4. 监控内存使用 :使用tracemalloc模块追踪内存分配
python 复制代码
# 监控内存使用示例
import tracemalloc

tracemalloc.start()

# 执行一些代码
data = [i for i in range(100000)]

# 获取内存快照
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')

print("\n内存分配排行:")
for stat in top_stats[:5]:
    print(stat)

tracemalloc.stop()
相关推荐
BrianGriffin1 小时前
JS異步:setTimeout包裝為sleep
开发语言·javascript·ecmascript
遇印记1 小时前
javaOCA考点(基础)
java·开发语言·青少年编程
李剑一1 小时前
Python学习笔记4
python
学困昇1 小时前
Linux基础开发工具(下):调试器gdb/cgdb的使用详解
linux·运维·服务器·开发语言·c++
biter down2 小时前
C++ 组合与继承:从设计本质到实战,吃透高内聚低耦合
开发语言·c++
灰灰勇闯IT2 小时前
C语言实战:字符串元音字母提取器的实现与优化
c语言·开发语言
fantasy5_52 小时前
C++11 核心特性实战博客
java·开发语言·c++
listhi5202 小时前
MOEAD算法实现详解(基于Python与MATLAB)
python·算法·matlab