标记整理算法
标记清除算法分为两个阶段:
- 标记阶段:遍历所有可达对象(从GC Root开始),标记它们为“存活”。
- 清除阶段:遍历整个堆内存,回收所有未被标记为“存活”的对象占用的空间。

算法步骤详解
-
暂停应用程序线程
- 垃圾回收过程通常需要暂停所有应用程序线程,这被称为 “Stop-The-World” 停顿。这是为了确保在标记过程中对象引用关系不会发生变化,保证垃圾回收的正确性。标记-清除算法的 STW 时间主要发生在标记阶段(也可能在清除阶段)。
-
标记阶段
起点:-
虚拟机栈(栈帧中的局部变量表)中引用的对象。
-
方法区中类静态属性引用的对象。
-
方法区中常量引用的对象(如字符串常量池里的引用)。
-
本地方法栈中 JNI(即 Native 方法)引用的对象。
-
所有被同步锁持有的对象。
-
JVM 内部引用(如系统类加载器、基本类型对应的 Class 对象)。
遍历:
-
访问一个对象。
-
将其标记为“存活”(通常是在对象头中设置一个标记位)。
-
递归地访问并标记这个对象引用的所有其他对象。
结果:
- 所有从 GC Roots 可达的对象都会被标记为“存活”。而无法从任何 GC Roots 访问到的对象,则被认为是“垃圾”。
-
-
清除阶段:
遍历堆:线性(或按某种顺序)遍历整个堆内存的地址空间。
回收垃圾:对于堆中的每一个位置(或对象):- 如果该位置的对象未被标记,则认为他是垃圾。
- 回收该对象占用的内存空间,回收通常不是立即擦除数据,而是将这个空间记录到一个空闲列表中,供后续新对象分配使用。
优点:
-
概念简单,易于理解: 算法流程非常直观。
-
实现相对容易: 早期的垃圾收集器常采用此算法。
-
无对象移动开销: 在清除阶段,存活对象的位置没有发生移动。这对于大对象或存活时间长的对象(如老年代对象)来说是个优势,避免了复制带来的开销。
-
空间利用率(理论上): 回收的内存可以立即放入空闲列表供分配。
缺点:
- 内存碎片:这是标记-清除算法最严重的问题。
- 效率问题:标记阶段需要遍历所有存活对象,清除阶段需要遍历整个堆(包括存活对象和垃圾对象)。对于大堆,这两个遍历过程都可能比较耗时,导致较长的 STW 停顿时间。
- 分配效率:由于存在内存碎片,分配新对象时需要遍历空闲列表(Free List)来寻找合适大小的空闲块。这比从连续空间(如复制算法中的 To 区)分配要慢。
- 空间浪费:碎片本身导致一部分空间无法被有效利用。

浙公网安备 33010602011771号