Python垃圾回收器如何追踪引用?及id()内置函数行为疑问
嘿,这两个问题都是Python入门时很容易困惑的点,我来帮你理清楚:
1. Python中的垃圾回收器如何追踪引用?
Python的垃圾回收主要靠引用计数作为核心机制,再配合其他辅助机制解决短板,具体追踪逻辑是这样的:
引用计数的基础追踪:每个Python对象的底层结构体里都维护着一个
ob_refcnt(引用计数器)。当你做这些操作时,计数器会自动变化:- 把对象赋值给变量、作为参数传给函数、添加到列表/字典等容器中 → 计数器+1
- 变量超出作用域、被重新赋值、用
del删除引用,或者容器中的对象被移除 → 计数器-1
当计数器降到0时,这个对象会被立即销毁,释放占用的内存。
解决循环引用的补充机制:引用计数搞不定循环引用(比如两个对象互相引用,A引用B,B引用A,它们的计数器永远不会到0)。所以Python还搭配了:
- 标记-清除算法:定期扫描所有对象,标记出所有能被全局变量、栈帧等“根对象”访问到的可达对象,然后清除所有不可达的对象(包括循环引用的对象)。
- 分代回收:根据对象的存活时间把它们分成3代(年轻代、中年代、老年代),对存活越久的对象,扫描频率越低——因为存活久的对象更可能是长期需要的,这样能减少垃圾回收的性能开销。
2. 关于
id()函数在数字、字符串上的行为困惑 你的示例完全符合Python的运行逻辑,咱们一步步拆解为什么会出现这样的结果:
首先看初始代码:
x = 100 y = x # x和y指向同一个值为100的对象 id(x) # 输出162569156 id(y) # 输出162569156
这一步很直观:x和y都引用了同一个整数对象,所以id()返回的内存地址完全相同——id()的作用就是返回对象在内存中的唯一标识。
然后你修改x的指向:
x = 200 # 此时x指向新对象,原对象的引用计数应减少 id(y) # 仍为162569156 id(x) # 变为162569932
这里的关键是Python的小对象缓存优化:
- 对于整数,Python默认会缓存
[-5, 256]范围内的所有整数对象。这些对象会被提前创建好,重复使用,而不是每次赋值都新建对象。所以你x = 100时,其实是直接引用了缓存池里的100对象;当x被重新赋值为200时,y仍然引用着缓存池里的100对象,它的引用计数从2降到了1(因为x不再引用它了),但计数没到0,所以对象不会被回收,id(y)自然保持不变。 - 对于字符串,Python也有**字符串驻留(interning)**机制:短字符串、或者符合标识符规则(比如只包含字母、数字、下划线)的字符串会被缓存,所以不同变量的相同字符串也可能共享同一个对象,返回相同的
id()。
你提到的引用计数机制在这里是完全生效的:原100对象的引用计数确实减少了1,但因为还有y在引用它,所以不会被垃圾回收。如果此时你执行del y,这个100对象的引用计数会降到0——不过因为它在缓存池里,Python通常不会真的销毁它,而是继续保留在缓存中,供后续代码复用。
内容的提问来源于stack exchange,提问作者Achal
相关产品推荐
相关产品推荐

