インクリメンタルGC
参考URL: http://members.at.infoseek.co.jp/zzyyb/gc/incremental-collector.html
program IncGC; //incremental update collector {$APPTYPE CONSOLE} uses Crt, SysUtils; type TIGCColor = (igccBlack, igccGray, igccWhite); TIGCObject = class; TIGCObjectQueue = record First, Last: TIGCObject; Count: Cardinal; end; TIGCObjectSpace = record Black: TIGCObjectQueue; Gray: TIGCObjectQueue; White: TIGCObjectQueue; end; TIGCObject = class(TObject) Pred, Succ: TIGCObject; References: array of TIGCObject; Color: TIGCColor; Root: Boolean; constructor Create(var ObjectSpace: TIGCObjectSpace); procedure AddReference(var ObjectSpace: TIGCObjectSpace; Obj: TIGCObject); procedure RemoveReference(var ObjectSpace: TIGCObjectSpace; Obj: TIGCObject); procedure SetRoot(var ObjectSpace: TIGCObjectSpace; Value: Boolean); end; procedure AddToObjectQueue(var ObjectQueue: TIGCObjectQueue; Obj: TIGCObject); begin Obj.Pred := ObjectQueue.Last; Obj.Succ := nil; if ObjectQueue.First = nil then ObjectQueue.First := Obj else ObjectQueue.Last.Succ := Obj; ObjectQueue.Last := Obj; Inc(ObjectQueue.Count); end; procedure RemoveFromObjectQueue(var ObjectQueue: TIGCObjectQueue; Obj: TIGCObject); function Check: Boolean; var I: TIGCObject; begin I := ObjectQueue.First; while I <> nil do begin if I = Obj then begin Result := True; Exit; end; I := I.Succ; end; Result := False; end; begin Assert(ObjectQueue.Count > 0); Assert(Check); if ObjectQueue.First = Obj then ObjectQueue.First := Obj.Succ else Obj.Pred.Succ := Obj.Succ; if ObjectQueue.Last = Obj then ObjectQueue.Last := Obj.Pred else Obj.Succ.Pred := Obj.Pred; Dec(ObjectQueue.Count); end; procedure ClearObjectQueue(var ObjectQueue: TIGCObjectQueue); var I, Next: TIGCObject; begin I := ObjectQueue.First; while I <> nil do begin Next := I.Succ; I.Free; I := Next; end; ObjectQueue.First := nil; ObjectQueue.Last := nil; ObjectQueue.Count := 0; end; procedure InitializeObjectSpace(out ObjectSpace: TIGCObjectSpace); begin FillChar(ObjectSpace, SizeOf(ObjectSpace), 0); end; procedure FinalizeObjectSpace(var ObjectSpace: TIGCObjectSpace); begin ClearObjectQueue(ObjectSpace.Black); ClearObjectQueue(ObjectSpace.Gray); ClearObjectQueue(ObjectSpace.White); end; procedure ProcessGC(var ObjectSpace: TIGCObjectSpace); var Obj, R: TIGCObject; I: Integer; begin if ObjectSpace.Gray.Count > 0 then begin WriteLn(ObjectSpace.Black.Count:10, ObjectSpace.Gray.Count:10, ObjectSpace.White.Count:10); {*} Obj := ObjectSpace.Gray.First; RemoveFromObjectQueue(ObjectSpace.Gray, Obj); Obj.Color := igccBlack; AddToObjectQueue(ObjectSpace.Black, Obj); for I := Low(Obj.References) to High(Obj.References) do begin R := Obj.References[I]; if (R <> nil) and (R.Color = igccWhite) then begin RemoveFromObjectQueue(ObjectSpace.White, R); R.Color := igccGray; AddToObjectQueue(ObjectSpace.Gray, R); end; end; end else begin WriteLn('*****************'); {*} WriteLn('**** Collect ****'); {*} WriteLn('*****************'); {*} ClearObjectQueue(ObjectSpace.White); while ObjectSpace.Black.Count > 0 do begin Obj := ObjectSpace.Black.First; RemoveFromObjectQueue(ObjectSpace.Black, Obj); if Obj.Root then begin Obj.Color := igccGray; AddToObjectQueue(ObjectSpace.Gray, Obj); end else begin Obj.Color := igccWhite; AddToObjectQueue(ObjectSpace.White, Obj); end end; end; end; constructor TIGCObject.Create(var ObjectSpace: TIGCObjectSpace); begin ProcessGC(ObjectSpace); ProcessGC(ObjectSpace); inherited Create; Color := igccWhite; AddToObjectQueue(ObjectSpace.White, Self); end; procedure TIGCObject.AddReference(var ObjectSpace: TIGCObjectSpace; Obj: TIGCObject); var I: Integer; begin if (Color = igccBlack) and (Obj.Color = igccWhite) then begin RemoveFromObjectQueue(ObjectSpace.White, Obj); Obj.Color := igccGray; AddToObjectQueue(ObjectSpace.Gray, Obj); end; for I := Low(References) to High(References) do begin if References[I] = nil then begin References[I] := Obj; Exit; end; end; SetLength(References, Length(References) + 1); References[High(References)] := Obj; end; procedure TIGCObject.RemoveReference(var ObjectSpace: TIGCObjectSpace; Obj: TIGCObject); var I: Integer; begin for I := Low(References) to High(References) do if References[I] = Obj then begin References[I] := nil; Exit; end; end; procedure TIGCObject.SetRoot(var ObjectSpace: TIGCObjectSpace; Value: Boolean); begin Root := Value; if Value and (Color = igccWhite) then begin RemoveFromObjectQueue(ObjectSpace.White, Self); Color := igccGray; AddToObjectQueue(ObjectSpace.Gray, Self); end; end; var ObjectSpace: TIGCObjectSpace; Root, Obj: TIGCObject; begin InitializeObjectSpace(ObjectSpace); Root := TIGCObject.Create(ObjectSpace); Root.SetRoot(ObjectSpace, True); repeat if Random(100) = 0 then begin Obj := TIGCObject.Create(ObjectSpace); Obj.SetRoot(ObjectSpace, True); Obj.AddReference(ObjectSpace, Root); Root.SetRoot(ObjectSpace, False); end else if Random(2) = 0 then begin Obj := TIGCObject.Create(ObjectSpace); Root.AddReference(ObjectSpace, Obj); end else if Root.References <> nil then begin Obj := Root.References[Random(Length(Root.References))]; if Obj <> nil then Root.RemoveReference(ObjectSpace, Obj); end; until False; FinalizeObjectSpace(ObjectSpace); end.
例によってソース貼って解説無しという悪癖発動。
これでオブジェクトの割り当てにこないだの固定長メモリアロケータを使えば、それなりに高速にできるかな…。
マルチスレッドにも対応させたいのですが、サッパリ分からん。新規オブジェクトが参照される前に他のスレッドで灰が全部処理されて白が消されたりとかありそう。新規を灰にしたところで他のスレッドで二巡したら終わりだし。新規は灰でしかもルートにして、何かに参照されてからルートを解除…とかやらないとだめなのでしょうか?