标记-清除算法是jvm最基础的gc算法,分标记和清除两步:先从gc roots出发标记存活对象,再扫描全堆回收未标记对象;不移动对象导致内存碎片,两次全堆遍历引发长stw停顿,故多用于老年代并常与整理算法结合。

标记-清除算法是 JVM 垃圾回收最基础的实现方式,它用两步完成内存清理:先标记所有存活对象,再统一回收未被标记的部分。
标记-清除算法的核心流程
它依赖可达性分析确定对象生死:从 GC Roots(如栈帧里的局部变量、静态字段、JNI 引用等)出发,遍历所有可达对象并打上“存活”标记;之后扫描整个堆,把没被标记的对象所占内存空间归入空闲列表,供后续分配使用。这个过程不移动对象,只做逻辑回收。
它为什么会产生内存碎片
清除操作只是把死亡对象的空间释放回空闲列表,并不整理位置。比如堆中原本连续的几块区域,A、B、C 三个对象依次排列,若 B 被回收,A 和 C 之间就留下一段空隙。随着多次 GC,空隙越碎越多,虽然总空闲空间足够,但找不到一块连续区域容纳大对象(例如一个 2MB 的数组),最终触发不必要的 Full GC,甚至抛出 OutOfMemoryError。
它的效率瓶颈在哪
标记和清除两个阶段都需要完整遍历堆内存。标记阶段要追踪所有存活对象的引用链,清除阶段要逐个检查每个对象是否被标记。堆越大,耗时越长;而且整个过程必须 Stop-The-World —— 应用线程全部暂停,用户请求卡住,接口响应飙升,CPU 却可能空转等待 GC 结束。
适用场景与现实取舍
它实现简单、内存开销小、不移动对象,适合对停顿敏感度不高或堆不太大的场景。但正因为碎片和效率问题,JVM 并不会单独把它用在主区域:年轻代弃用它,改用复制算法;老年代则搭配标记-整理算法来压缩空间。它更多作为分代收集策略中的底层组件之一,或出现在某些低延迟 GC(如 ZGC 的部分阶段)的设计思想里。
不复杂但容易忽略











