标记清除法顾名思义,标记了,然后清除。这样一来会有大量的内存碎片。虽然如此,不过也有一些垃圾回收器采用他,例如现在最流行的 CMS 垃圾回收器。
算法原理
《深入理解 Java 虚拟机》中写道:
首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
这句话其实个人是比较怀疑的。因为一般来说是先标记出还在活动的对象,因为可以根据 GC roots 推出,而不是标记除需要回收的对象,因为个人感觉你一开始是不知道需要回收的对象的。
伪代码实现
这里主要还是参考《垃圾回收的算法与实现》:
标记阶段
mark_phase(){
for(r : $roots){
mark(*r)
}
}
mark(obj){
if(obj.mark == FALSE){
obj.mark = TRUE
for(child : children(obj)){
mark(*child)
}
}
}
标记还是比较简单的。也就是遍历每一个根节点,由于每个根节点是一个树,这个时候可以采用广度优先算法或者深度优先算法来遍历这棵树,标记出所有的节点。
清除阶段
sweep_phase(){
sweeping = $heap_start
while(sweeping < $heap_end){
if(sweeping.mark == TRUE){
sweeping.mark = FALSE
} else {
sweeping.next = $free_list
$free_list = sweeping
}
sweeping += sweeping.size
}
}
这里主要是为了把清除的内存标记到空闲链表中,用于下一次分配内存。
优缺点
内存碎片问题
消耗的时间
和复制算法差不多
标记的时候需要遍历 GC roots,标记出活动对象。
清除的时候需要遍历整个堆
最后
感觉标记清除法不太好,比如说有大量碎片,以及再分配内存的时候又要遍历空闲链表,那么为什么 CMS 垃圾收集器还要用这个呢?