垃圾回收算法核心是标记-清除、复制、引用计数和可达性分析四种。标记-清除分标记与清除两步,简洁但易产生碎片;复制算法避免碎片但浪费空间,多用于新生代;引用计数实时高效却无法处理循环引用;可达性分析以gc roots为起点遍历,彻底解决循环引用,为现代主流方案。

简单来说,垃圾回收算法的核心就两步:先找出哪些对象已经没用了,再把它们占的内存清掉,腾出来给新对象用。
标记-清除:最基础的思路
这是所有自动回收算法的起点。它分两个阶段:
- 标记阶段:从程序里还在用的变量(比如全局变量、栈上的局部引用)出发,顺着引用关系一路找下去,把所有“能被访问到”的对象打上标记;
- 清除阶段:把没被打上标记的对象直接删掉,释放它们占用的内存。
优点是逻辑直白、不用移动对象;缺点是清理后容易留下很多零散的小空隙(内存碎片),后续分配大对象时可能找不到连续空间,被迫提前再回收一次。
复制算法:适合“短命”对象多的场景
它把内存分成两块,每次只用其中一块。当这块快用完时:
- 把当前还活着的对象全部复制到另一块空内存上;
- 然后把原来那整块内存一次性清空。
这样既没碎片,又省去了逐个清理的开销。但代价是总得留一半内存闲置着——所以主流虚拟机只把它用在新生代(那里98%的对象活不过几次回收)。
引用计数:直观但有硬伤
每个对象自己记着被多少地方引用着。每当有新引用指向它,计数+1;引用断开,计数-1。一旦计数归零,立刻回收。
好处是反应快、实现简单;坏处是解决不了循环引用——比如A引用B,B又引用A,哪怕外界没人再用它们,计数永远不为0,内存就一直卡着不放。
可达性分析:现代主流的判断依据
不靠对象自己报数,而是从一组明确的“根对象”(GC Roots)开始查。这些根通常是正在执行的方法里的局部变量、静态字段、JNI引用等。
只要一个对象能通过引用链从任意一个根到达,它就算“可达”,必须保留;否则就判定为垃圾。这个方法天然绕开了循环引用问题,是JVM、.NET、Python等主流运行时采用的基础策略。










