Python中的del语句与垃圾回收机制深度解析

在 Python 中,del 语句经常被误认为是“直接删除对象”。实际上它与 Python 的 垃圾回收(Garbage Collection, GC)机制之间存在较深层的关系。理解这一点需要从 对象引用、引用计数、循环引用以及 GC 机制几个层面来看。


一、del语句的本质:删除引用,而不是对象

在 Python 中,一切皆对象,而变量只是 对象的引用(reference)

del 的作用是:
👉 删除变量名与对象之间的绑定关系(引用)

示例:

a = [1, 2, 3]
b = a
del a

此时:

  • a 被删除
  • 列表对象 [1,2,3] 仍然存在
  • 因为 b 仍然引用它

内存结构可以理解为:

a ----\
       --> [1,2,3]
b ----/

执行:

del a

变成:

b ----> [1,2,3]

因此:

del 删除的是 变量引用,而不是对象本身。


二、Python垃圾回收的核心机制:引用计数

Python最基础的垃圾回收机制是 引用计数(Reference Counting)

每个对象维护一个 引用计数器

对象引用数 +1
对象引用数 -1

当:

引用计数 == 0

对象会 立即被释放内存

示例:

a = [1,2,3]  # ref = 1
b = a        # ref = 2
del a        # ref = 1
del b        # ref = 0 -> 对象释放

内部逻辑:

Py_DECREF(obj)
if refcount == 0:
    free(obj)

优点:

  • 回收速度快
  • 实时回收

缺点:

  • 无法处理循环引用

三、循环引用问题

循环引用是引用计数机制最大的缺陷。

示例:

class Node:
    pass

a = Node()
b = Node()

a.ref = b
b.ref = a

del a
del b

对象结构:

a -> NodeA -> NodeB
      ^        |
      |________|

即使:

del a
del b

仍然存在:

NodeA.ref -> NodeB
NodeB.ref -> NodeA

引用计数:

NodeA = 1
NodeB = 1

所以:

👉 引用计数永远不会变成 0

这就造成了 内存泄漏


四、Python的解决方案:分代垃圾回收(Generational GC)

为了处理循环引用,Python引入了 GC模块

相关模块:

import gc

Python GC 使用 分代回收策略(Generational Garbage Collection)

对象被分为三代:

特点
Generation 0新创建对象
Generation 1存活较久对象
Generation 2长期对象

回收逻辑:

新对象 -> Gen0
多次存活 -> Gen1
长期存活 -> Gen2

GC 只会 扫描可能存在循环引用的容器对象

  • list
  • dict
  • set
  • class instance

而不会扫描:

  • int
  • str
  • float

因为它们不可能形成循环引用。


五、del 与 GC 的协同关系

del 的作用可以理解为 触发引用计数减少

del x

内部逻辑:

x 引用计数 -1

可能产生三种结果:

情况结果
ref > 0对象继续存在
ref = 0立即释放
循环引用等待 GC

六、del的高级用法

1 删除列表元素

a = [1,2,3,4]
del a[1]

结果:

[1,3,4]

本质:

调用 list 的 C API 删除元素

2 删除切片

a = [1,2,3,4,5]
del a[1:3]

结果:

[1,4,5]

3 删除字典键

d = {"a":1,"b":2}
del d["a"]

4 删除对象属性

class A:
    pass

obj = A()
obj.x = 10

del obj.x

5 删除变量

del x

之后访问:

NameError

七、del vs None

很多人混淆:

del x

x = None

区别:

操作含义
del x删除变量
x = None变量仍存在,但指向 None

示例:

x = [1,2,3]

x = None

引用变化:

[1,2,3] refcount -1

但变量 x 仍然存在。

而:

del x

变量也不存在。


八、GC模块调试

查看GC状态:

import gc

gc.get_count()

查看阈值:

gc.get_threshold()

手动触发:

gc.collect()

关闭GC:

gc.disable()

九、底层实现(CPython)

在 CPython 中:

每个对象头部包含:

PyObject
{
    ob_refcnt
    ob_type
}

引用计数:

ob_refcnt

宏操作:

Py_INCREF(obj)
Py_DECREF(obj)

当:

Py_DECREF -> 0

调用:

tp_dealloc

释放对象。


十、性能与最佳实践

1 不要滥用 del

Python 会自动 GC。

只有在以下情况使用:

  • 大对象
  • 大数组
  • 大缓存

示例:

del big_dataframe

2 避免循环引用

推荐:

weakref

模块:

import weakref

3 使用上下文管理

with open("file.txt") as f:
    data = f.read()

资源自动释放。


十一、经典面试题总结

Q1:del 是否删除对象?

答:

不是

删除的是 引用绑定


Q2:Python为什么需要GC?

因为:

引用计数无法处理循环引用

Q3:什么时候对象被释放?

refcount == 0
或
GC发现循环引用

十二、一句话总结

del + 引用计数 + 分代GC 构成了 Python 的完整内存管理体系:

del → 减少引用
引用计数 → 立即回收
GC → 解决循环引用

如果你愿意,我可以继续深入讲三个更高级但很少有人真正讲清楚的内容:

1️⃣ Python GC 三代算法源码解析(90%工程师不懂)
2️⃣ 为什么 Python 的 GC 比 Java GC 简单得多
3️⃣ __del__ 析构函数导致的 GC 灾难(真实生产问题)

文章已创建 5074

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部