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));