MarkSweep是Hot Spot用于收集老年代的算法
在Hot Spot里,新生代用copy算法来收集,cheney算法是copy算法的一种,老年代用MarkSweep算法收集垃圾,这两种算法都属于跟踪收集器,即通过目标对象是否可达来判断是否是需要收集的垃圾。
关于这两种算法的大致描述,在 JVM的垃圾回收有描述。
对于MarSweep的大致实现原理和代码,在Baby’s First Garbage Collector已经有很详细的描述了。
不过到底自己看资料吸收了多少,只有动手实现一次才能知道,所以就有了这篇博客作为记录。
因为在Baby’s First Garbage Collector作者已经用C语言实现了一次,因为不想写着写着就和他写的代码是一样的了,所以我是C#实现了一次,但不管用C#还是用Java实现都是有问题的,因为我没办法自己控制内存的分配和销毁,不过我的目的是为了理清算法的逻辑,如果有需要再换种语言来写吧。
MarkSweep算法的主要有两个步骤,第一步是标记(Mark),第二步是整理(Sweep)。而实现也只需要两个结构,一个是栈,以用来模拟虚拟机栈,进入栈中的对象都是活跃的(或者说可达的)对象,另一个是一条链表,这条链表上记录所有被已被创建的对象。
我们需要一个标志来表示某个对象是否是可达的,可以通过在定义对象时设置一个标志位,也可以用位图来完成。
当完成标记(Mark)过程,进入整理(Sweep)逻辑,我们只需要遍历之前定义的那条链表,释放掉链表中未被标记可达的对象即完成垃圾回收了。整个MarkSweep算法的逻辑就是如此。
MarkSweep类代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MarkSweep
{
public class MarkSweep
{
/// <summary>
/// GC前创建对象数
/// </summary>
public int totalNmuBeforeGc;
/// <summary>
/// 设置已被扫描
/// </summary>
/// <param name="model">对象</param>
private void Mark(Model model)
{
if (model == null || model.marked)
{
return;
}
model.marked = true;
Mark(model.next);
}
/// <summary>
/// 对栈中对象设置已被扫描
/// </summary>
/// <param name="stack">模拟的虚拟机栈</param>
private void MarkAll(VMStack vm)
{
for (int i = 0; i < vm.Index; i++)
{
this.Mark(vm.stack[i]);
}
}
/// <summary>
/// 整理
/// </summary>
/// <param name="vm">模拟的虚拟机栈</param>
private void Sweep()
{
//// head对此线程应是全局唯一的 对其他线程而言不是 因为VMStack不是static的
Model head = VMStack.Head;
Model entry = head.next;
Model forward = head;
//// 开始遍历所有已创建对象
while (entry != null)
{
//// 如果没有被扫描标记 说明此对象已经不可达 释放掉
if (!entry.marked)
{
//// 处理指针
forward.next = entry.next;
entry.next = null;
//// 在这里完成free操作
//// free(entry)
entry = forward.next;
Model.createdModelNum--;
}
else
{
//// 更新指针
entry = entry.next;
forward = forward.next;
}
}
}
/// <summary>
/// GC
/// </summary>
/// <param name="vm">模拟的虚拟机栈</param>
public void GC(VMStack vm)
{
this.totalNmuBeforeGc = Model.createdModelNum;
this.MarkAll(vm);
this.Sweep();
}
}
}
定义模拟虚拟机栈:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MarkSweep
{
public class VMStack
{
/// <summary>
/// 栈最大深度
/// </summary>
public const int stackMaxDepth = 1024;
/// <summary>
/// 定义栈
/// </summary>
public Model[] stack = new Model[stackMaxDepth];
/// <summary>
/// 已创建对象链表尾指针
/// </summary>
public static Model Tail = new Model();
/// <summary>
/// 已创建对象链表头指针,当尾指针后移时,头指针始终指向和尾指针第一次指向的对象.
/// </summary>
public static Model Head = Tail;
/// <summary>
/// 定义索引
/// </summary>
private static int index = 0;
/// <summary>
/// 封装
/// </summary>
public int Index
{
get { return index; }
private set { index = value; }
}
/// <summary>
/// 出栈
/// </summary>
/// <returns></returns>
public object Pop()
{
if (index == 0)
{
throw new Exception("stack under flow");
}
return this.stack[index--];
}
/// <summary>
/// 入栈
/// </summary>
/// <param name="model">待入栈对象</param>
public void Push(Model model)
{
if(index == stackMaxDepth - 1)
{
throw new Exception("stack over flow");
}
model.marked = true;
this.stack[index] = model;
index++;
}
}
}
定义模拟对象:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MarkSweep
{
/// <summary>
/// 假设这个对象是通用对象
/// </summary>
public class Model
{
/// <summary>
/// 值
/// </summary>
public int intValue;
/// <summary>
/// 维护一条所有已创建对象链表
/// </summary>
public Model next;
/// <summary>
/// 是否已被引用
/// </summary>
public bool marked;
/// <summary>
/// 已创建对象数量
/// </summary>
public static int createdModelNum = 0;
/// <summary>
/// 构造函数 并维护到已创建对象链表
/// </summary>
public Model()
{
this.intValue = 0;
this.marked = false;
this.next = null;
//// 头结点作为标志位不含值 先为头结点初始化 实例化后再做指针操作
if (VMStack.Tail != null)
{
VMStack.Tail.next = this;
VMStack.Tail = this;
createdModelNum++;
}
}
}
}
这样即可完成调用:
MarkSweep ms = new MarkSweep();
ms.GC(stack);
Console.WriteLine(string.Format("total model num: {0}, after gc, remain model num: {1}", ms.totalNmuBeforeGc, Model.createdModelNum));