Python以引用计数为主、分代回收为辅:引用计数为0时立即销毁对象,但循环引用需分代回收(基于DFS的标记-清除)兜底;内存释放发生在堆区,不立即归还操作系统。

对象内存销毁不是程序员手动触发的动作,而是垃圾回收机制在后台自动完成的结果。关键不在于“谁来删”,而在于“什么时候能删”和“凭什么能删”。
对象什么时候算“可以被销毁”
核心判断标准是是否还被程序逻辑有效引用。不同语言策略不同:
- Python 主要靠引用计数:只要一个对象的引用数归零,就立即销毁;但遇到循环引用(比如 A 持有 B,B 又持有 A),引用数卡在 1,这时就得靠后面的标记-清除算法兜底
- Java 完全不用引用计数,而是用可达性分析:从线程栈、静态变量这些“根”出发,顺着引用链往下找;找不到的对象,哪怕互相引用,也直接判定为可回收
销毁过程不是简单“擦掉”,而是分阶段回收
内存释放不是一拍即合的事,通常包含识别、清理、整理三步:
- 标记:GC 扫描所有存活对象,把它们打上“有用”标签;没被打标的,就是待回收对象
- 清除或复制:有的 GC 直接清空标记外的内存(标记-清除);有的把存活对象集中搬到一块新区域,再整块清空旧空间(复制算法)
- 整理(可选):为避免内存碎片,部分 GC 会把存活对象往一端挤压,腾出连续大块空间(标记-压缩)
销毁发生在哪些内存区域
不是所有内存都归 GC 管:
- 堆(Heap):对象实例的主要栖息地,是 GC 的主战场
- 方法区/元空间:存放类定义、常量、静态变量等,也会被 GC 清理(比如卸载不再使用的类)
- 栈、程序计数器、本地方法栈:随线程创建和结束自动释放,不走 GC 流程
销毁不等于立刻归还给操作系统
GC 回收的是 JVM 堆内的内存空间,但这些空间通常不会马上交还给操作系统:
- JVM 会把刚回收的内存先留着,供后续 new 对象快速分配,减少系统调用开销
- 只有堆长期闲置、且配置了特定参数(如 -XX:+AlwaysPreTouch 或某些 G1/ZGC 的行为),才可能主动返还
- 所以看到“堆内存已回收”,不代表进程 RSS 内存下降——这是常见误解

















