变量释放的本质是引用计数归零触发内存回收,取决于是否存在其他访问路径;变量是对象的标签,赋值不复制数据而增加引用;循环引用需gc模块或weakref处理。

变量释放的本质,是让对象的引用计数归零,从而触发内存回收。它不依赖“删除变量”这个动作本身,而取决于还有没有其他路径能访问到该对象。
看清变量与对象的关系
Python中变量只是标签,对象才是真实存在的数据实体。赋值不是复制数据,而是增加一个指向同一对象的标签。
-
a = [1, 2, 3] → 创建列表对象,
a是第一个引用,refcount = 1 -
b = a →
b也指向该列表,refcount 变为 2 - del a 或 a = None → refcount 减为 1,对象仍在内存中
- del b → refcount 归零,列表对象立即被销毁,内存释放
识别哪些操作真正影响引用计数
不是所有赋值或修改都会改变计数;关键看是否新建对象或断开连接。
-
共享引用操作(如
c = a)→ refcount +1 -
重新赋值(如
a = "new")→ 原对象 refcount -1,新字符串 refcount +1 -
就地修改(如
a.append(4))→ 不影响 refcount,仍指向原列表 -
函数返回值赋值(如
res = func())→ 若函数返回的是已有对象,refcount +1;若新建对象,则原对象不受影响
处理循环引用:引用计数失效时的补救
当两个对象互相持有对方(如 A.b = B 且 B.a = A),它们的 refcount 永远 ≥1,无法靠引用计数释放。
- 用
gc.disable()和gc.collect()验证是否为循环引用导致内存未释放 - 调用
gc.get_referrers(obj)查看谁在引用该对象,辅助定位环路 - 对已知结构(如树节点、观察者模式),显式置空引用:
node.parent = None - 必要时使用
weakref替代强引用,从源头避免循环
验证释放是否生效的实用方法
别只看 del 是否执行成功,要确认对象是否真被清理。
- 用
sys.getrefcount(obj) - 1查当前 refcount(注意该函数调用本身会临时+1) - 用
id(obj)对比前后地址,确认是否创建了新对象 - 结合
memory_profiler或tracemalloc观察内存变化趋势 - 在长生命周期服务中,定期调用
gc.collect()并检查gc.garbage是否有残留










