684 lines
23 KiB
C#
684 lines
23 KiB
C#
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
|
||
|
/*******************************************************************/
|
||
|
/* WARNING */
|
||
|
/* This file should be identical in the Bartok and Singularity */
|
||
|
/* depots. Master copy resides in Bartok Depot. Changes should be */
|
||
|
/* made to Bartok Depot and propagated to Singularity Depot. */
|
||
|
/*******************************************************************/
|
||
|
|
||
|
|
||
|
// #define DEBUG
|
||
|
|
||
|
namespace System.GCs {
|
||
|
|
||
|
using Microsoft.Bartok.Options;
|
||
|
using Microsoft.Bartok.Runtime;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Threading;
|
||
|
using System.Collections;
|
||
|
|
||
|
[NoCCtor]
|
||
|
[RequiredByBartok]
|
||
|
internal class ARCSCollector : SingleThreadedRCCollector {
|
||
|
[PreInitRefCounts]
|
||
|
public new static void Initialize() {
|
||
|
SingleThreadedRCCollector.Initialize();
|
||
|
|
||
|
incrementer =
|
||
|
(RefCountIncrementer)BootstrapMemory.
|
||
|
Allocate(typeof(RefCountIncrementer));
|
||
|
decrementer =
|
||
|
(RefCountDecrementer)BootstrapMemory.
|
||
|
Allocate(typeof(RefCountDecrementer));
|
||
|
|
||
|
instance =
|
||
|
(ARCSCollector)BootstrapMemory.
|
||
|
Allocate(typeof(ARCSCollector));
|
||
|
}
|
||
|
|
||
|
[NoInline]
|
||
|
[ManualRefCounts]
|
||
|
public override void VerifyHeap(bool beforeCollection) {
|
||
|
preVerifyHeap();
|
||
|
|
||
|
// Deallocate objects on the delayed deallocation list.
|
||
|
purgeDeallocationList(decrementer);
|
||
|
|
||
|
postVerifyHeap(beforeCollection);
|
||
|
}
|
||
|
|
||
|
|
||
|
[MixinConditional("ARCSGC")]
|
||
|
[Mixin(typeof(Object))]
|
||
|
internal class ARCSGCObject : System.Object {
|
||
|
internal new TrialDeletion.PreHeader preHeader;
|
||
|
}
|
||
|
|
||
|
|
||
|
internal override void EnableHeap() {
|
||
|
}
|
||
|
|
||
|
internal override void DestructHeap() {
|
||
|
if (VTable.enableGCVerify) {
|
||
|
VerifyHeap(true);
|
||
|
}
|
||
|
base.DestructHeap();
|
||
|
if (VTable.enableGCProfiling) {
|
||
|
if (RCCollector.ProfilingMode) {
|
||
|
EmitRefCountsProfile();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[NoInline]
|
||
|
[InlineIntoOnce]
|
||
|
[ManualRefCounts]
|
||
|
[RequiredByBartok]
|
||
|
internal static unsafe void NonNullIncrementRefCount(Object obj) {
|
||
|
uint refState = BasicIncrementRefCount(obj);
|
||
|
|
||
|
td.IncrementCycleProcessing(refState, obj);
|
||
|
}
|
||
|
|
||
|
// Exclude the object from leaked cycle processing.
|
||
|
if ((refState & RSMasks.acyclicFlag) != 0) {
|
||
|
#if DEBUG
|
||
|
PLCLink* pLinkAddr = td.getPLCLink(obj);
|
||
|
VTable.Assert(pLinkAddr == null,
|
||
|
@"pLinkAddr == null");
|
||
|
#endif // DEBUG
|
||
|
} else {
|
||
|
PLCLink* pLinkAddr = td.getPLCLink(obj);
|
||
|
// If the object hasn't already been removed from
|
||
|
// the PLC ("potentially leaked cycle") list, remove it.
|
||
|
if (pLinkAddr != null) {
|
||
|
// Reset the PLC link pointer in the object.
|
||
|
td.setPLCLink(obj, null);
|
||
|
td.removeFromPLCList(pLinkAddr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
[InlineIntoOnce]
|
||
|
[ManualRefCounts]
|
||
|
[RequiredByBartok]
|
||
|
internal static void AcyclicIncrementRefCount(Object obj) {
|
||
|
if (obj == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
NonNullAcyclicIncrementRefCount(obj);
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
[InlineIntoOnce]
|
||
|
[ManualRefCounts]
|
||
|
[RequiredByBartok]
|
||
|
internal static void NonNullAcyclicIncrementRefCount(Object obj) {
|
||
|
uint refState = BasicIncrementRefCount(obj);
|
||
|
|
||
|
VTable.Assert((refState & RSMasks.acyclicFlag) != 0,
|
||
|
@"(refState & RSMasks.acyclicFlag) != 0");
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
[ManualRefCounts]
|
||
|
[RequiredByBartok]
|
||
|
internal static uint BasicIncrementRefCount(Object obj) {
|
||
|
VTable.Assert(obj != null &&
|
||
|
(obj.REF_STATE & RSMasks.refCount) <
|
||
|
RSMasks.refCount,
|
||
|
@"obj != null &&
|
||
|
(obj.REF_STATE & RSMasks.refCount) <
|
||
|
RSMasks.refCount");
|
||
|
|
||
|
uint refState = obj.REF_STATE;
|
||
|
if ((refState & countingONFlagMask) == 0) {
|
||
|
return;
|
||
|
}
|
||
|
VTable.Assert(refState != 0,
|
||
|
@"refState != 0");
|
||
|
|
||
|
obj.REF_STATE = refState+1;
|
||
|
|
||
|
return refState;
|
||
|
}
|
||
|
|
||
|
|
||
|
[NoInline]
|
||
|
[ManualRefCounts]
|
||
|
[RequiredByBartok]
|
||
|
internal static unsafe void NonNullDecrementRefCount(Object obj) {
|
||
|
VTable.Assert(obj != null &&
|
||
|
(obj.REF_STATE & RSMasks.refCount) > 0,
|
||
|
@"obj != null &&
|
||
|
(obj.REF_STATE & RSMasks.refCount) > 0");
|
||
|
|
||
|
uint refState = obj.REF_STATE;
|
||
|
if ((refState & RSMasks.refCount) == 1) {
|
||
|
VTable.Assert((refState & RSMasks.countingFlag) != 0,
|
||
|
@"(refState & RSMasks.countingFlag) != 0");
|
||
|
|
||
|
MultiUseWord muw = MultiUseWord.GetForObject(obj);
|
||
|
if (muw.IsMonitorOrInflatedTag()) {
|
||
|
MultiUseWord.RefCountGCDeadObjHook(muw);
|
||
|
}
|
||
|
|
||
|
// Set aside the object for a delayed deallocation.
|
||
|
deallocateLazily(obj);
|
||
|
} else if ((refState & RSMasks.countingFlag) == 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
obj.REF_STATE = refState-1;
|
||
|
}
|
||
|
|
||
|
[NoInline]
|
||
|
[InlineIntoOnce]
|
||
|
[ManualRefCounts]
|
||
|
[RequiredByBartok]
|
||
|
internal static unsafe void DecrementRefCount(Object obj) {
|
||
|
if (obj == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
NonNullDecrementRefCount(obj);
|
||
|
}
|
||
|
|
||
|
|
||
|
[RequiredByBartok]
|
||
|
internal static void IncrementReferentRefCounts(UIntPtr objAddr,
|
||
|
VTable vtable) {
|
||
|
UpdateReferentRefCounts(objAddr, vtable, incrementer);
|
||
|
}
|
||
|
|
||
|
[RequiredByBartok]
|
||
|
internal static void DecrementReferentRefCounts(UIntPtr objAddr,
|
||
|
VTable vtable) {
|
||
|
UpdateReferentRefCounts(objAddr, vtable, decrementer);
|
||
|
}
|
||
|
|
||
|
[ManualRefCounts]
|
||
|
internal static unsafe void IncrementReferentRefCounts
|
||
|
(UIntPtr objAddr,
|
||
|
VTable vtable,
|
||
|
int start,
|
||
|
int span) {
|
||
|
UpdateReferentRefCounts(objAddr, vtable, start, span,
|
||
|
incrementer);
|
||
|
}
|
||
|
|
||
|
[ManualRefCounts]
|
||
|
internal static unsafe void DecrementReferentRefCounts
|
||
|
(UIntPtr objAddr,
|
||
|
VTable vtable,
|
||
|
int start,
|
||
|
int span) {
|
||
|
UpdateReferentRefCounts(objAddr, vtable, start, span,
|
||
|
decrementer);
|
||
|
}
|
||
|
|
||
|
|
||
|
[Inline]
|
||
|
[ManualRefCounts]
|
||
|
protected static void deallocateObjects() {
|
||
|
deallocateObjects(decrementer);
|
||
|
}
|
||
|
|
||
|
[NoInline]
|
||
|
[ManualRefCounts]
|
||
|
protected static void purgeDeallocationList() {
|
||
|
deallocateObjects(decrementer);
|
||
|
}
|
||
|
|
||
|
|
||
|
[ManualRefCounts]
|
||
|
private static UIntPtr getDfsFinishingTime(Object obj) {
|
||
|
return ((RCGCVerificationObject)obj).preHeader.
|
||
|
dfsFinishingTime;
|
||
|
}
|
||
|
|
||
|
[ManualRefCounts]
|
||
|
private static void setDfsFinishingTime(Object obj,
|
||
|
UIntPtr time) {
|
||
|
((RCGCVerificationObject)obj).preHeader.dfsFinishingTime =
|
||
|
time;
|
||
|
}
|
||
|
|
||
|
|
||
|
private class RefCountIncrementer : NonNullReferenceVisitor {
|
||
|
[ManualRefCounts]
|
||
|
internal override unsafe void Visit(UIntPtr* loc) {
|
||
|
UIntPtr objAddr = *loc;
|
||
|
Object obj = Magic.fromAddress(objAddr);
|
||
|
IncrementRefCount(obj);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private class RefCountDecrementer : NonNullReferenceVisitor {
|
||
|
[ManualRefCounts]
|
||
|
internal override unsafe void Visit(UIntPtr* loc) {
|
||
|
UIntPtr objAddr = *loc;
|
||
|
Object obj = Magic.fromAddress(objAddr);
|
||
|
DecrementRefCount(obj);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
private abstract class ObjectVisitor :
|
||
|
SegregatedFreeList.ObjectVisitor {
|
||
|
[ManualRefCounts]
|
||
|
internal override void VisitSmall(Object obj,
|
||
|
UIntPtr memAddr) {
|
||
|
this.Visit(obj);
|
||
|
}
|
||
|
|
||
|
[ManualRefCounts]
|
||
|
internal override UIntPtr VisitLarge(Object obj) {
|
||
|
return this.Visit(obj);
|
||
|
}
|
||
|
|
||
|
internal abstract override UIntPtr Visit(Object obj);
|
||
|
}
|
||
|
|
||
|
|
||
|
private class BackupInitializer : ObjectVisitor {
|
||
|
[ManualRefCounts]
|
||
|
internal override UIntPtr Visit(Object obj) {
|
||
|
setBackupRefcount(obj, UIntPtr.Zero);
|
||
|
|
||
|
VTable vtable = obj.vtable;
|
||
|
return ObjectLayout.
|
||
|
ObjectSize(Magic.addressOf(obj), vtable);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private class BackupRefCount : NonNullReferenceVisitor {
|
||
|
[ManualRefCounts]
|
||
|
internal override unsafe void Visit(UIntPtr* loc) {
|
||
|
UIntPtr addr = *loc;
|
||
|
|
||
|
UIntPtr page = PageTable.Page(addr);
|
||
|
if (!PageTable.IsGcPage(page)) {
|
||
|
PageType pageType = PageTable.Type(page);
|
||
|
VTable.Assert(pageType == PageType.NonGC ||
|
||
|
pageType == PageType.Stack,
|
||
|
@"pageType == PageType.NonGC ||
|
||
|
pageType == PageType.Stack");
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
UIntPtr objAddr = SegregatedFreeList.Find(addr);
|
||
|
incrementBackupRefCount.Traverse(objAddr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private class IncrementBackupRefCount : NonNullReferenceVisitor {
|
||
|
[ManualRefCounts]
|
||
|
internal void Traverse(UIntPtr objAddr) {
|
||
|
Object obj = Magic.fromAddress(objAddr);
|
||
|
UIntPtr count = getBackupRefcount(obj);
|
||
|
setBackupRefcount(obj, count+1);
|
||
|
if (obj.GcMark((UIntPtr)1)) {
|
||
|
this.VisitReferenceFields(obj);
|
||
|
}
|
||
|
while (!this.workList.IsEmpty) {
|
||
|
objAddr = this.workList.Read();
|
||
|
obj = Magic.fromAddress(objAddr);
|
||
|
this.VisitReferenceFields(obj);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[ManualRefCounts]
|
||
|
internal override unsafe void Visit(UIntPtr* loc) {
|
||
|
UIntPtr addr = *loc;
|
||
|
|
||
|
UIntPtr page = PageTable.Page(addr);
|
||
|
if (!PageTable.IsGcPage(page)) {
|
||
|
PageType pageType = PageTable.Type(page);
|
||
|
VTable.Assert(pageType == PageType.NonGC ||
|
||
|
pageType == PageType.Stack,
|
||
|
@"pageType == PageType.NonGC ||
|
||
|
pageType == PageType.Stack");
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
UIntPtr objAddr = SegregatedFreeList.Find(addr);
|
||
|
Object obj = Magic.fromAddress(objAddr);
|
||
|
UIntPtr count = getBackupRefcount(obj);
|
||
|
setBackupRefcount(obj, count+1);
|
||
|
if (obj.GcMark((UIntPtr)1)) {
|
||
|
this.workList.Write(objAddr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private UIntPtrQueue workList;
|
||
|
}
|
||
|
|
||
|
private class RootsScanner : NonNullReferenceVisitor {
|
||
|
internal void Initialize(NonNullReferenceVisitor v) {
|
||
|
this.visitor = v;
|
||
|
}
|
||
|
|
||
|
[ManualRefCounts]
|
||
|
internal override unsafe void Visit(UIntPtr* loc) {
|
||
|
UIntPtr pageLoc = PageTable.Page((UIntPtr)loc);
|
||
|
PageType pageType = PageTable.Type(pageLoc);
|
||
|
if (pageType != PageType.NonGC &&
|
||
|
pageType != PageType.Stack) {
|
||
|
VTable.Assert(PageTable.IsGcPage(pageLoc),
|
||
|
@"PageTable.IsGcPage(pageLoc)");
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
uint addr = (uint)*loc;
|
||
|
if (pageType == PageType.NonGC || (addr & 0x03) == 0) {
|
||
|
this.visitor.Visit(loc);
|
||
|
}
|
||
|
if (pageType == PageType.Stack) {
|
||
|
*loc = (UIntPtr)(addr | 0x01);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NonNullReferenceVisitor visitor;
|
||
|
}
|
||
|
|
||
|
private class ResetRoots : NonNullReferenceVisitor {
|
||
|
[ManualRefCounts]
|
||
|
internal override unsafe void Visit(UIntPtr* loc) {
|
||
|
UIntPtr pageLoc = PageTable.Page((UIntPtr)loc);
|
||
|
PageType pageType = PageTable.Type(pageLoc);
|
||
|
if (pageType != PageType.NonGC &&
|
||
|
pageType != PageType.Stack) {
|
||
|
VTable.Assert(PageTable.IsGcPage(pageLoc),
|
||
|
@"PageTable.IsGcPage(pageLoc)");
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (pageType == PageType.Stack) {
|
||
|
*loc = (UIntPtr)((uint)*loc & 0xfffffffc);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private class LeakAccumulator : ObjectVisitor {
|
||
|
internal UIntPtr Size;
|
||
|
|
||
|
internal void Initialize() {
|
||
|
this.Size = UIntPtr.Zero;
|
||
|
}
|
||
|
|
||
|
[ManualRefCounts]
|
||
|
internal override unsafe UIntPtr Visit(Object obj) {
|
||
|
VTable vtable = obj.vtable;
|
||
|
UIntPtr size =
|
||
|
ObjectLayout.ObjectSize(Magic.addressOf(obj), vtable);
|
||
|
|
||
|
uint refState = obj.REF_STATE;
|
||
|
UIntPtr refCount = (UIntPtr)(refState & RSMasks.refCount);
|
||
|
if ((refState & RSMasks.countingFlag) != 0 &&
|
||
|
refCount > 0) {
|
||
|
// This object is considered live by the
|
||
|
// RC collector.
|
||
|
UIntPtr count = getBackupRefcount(obj);
|
||
|
if (count == 0) {
|
||
|
// But it is actually unreachable.
|
||
|
this.Size += size;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private class LeakedNodesDFS : ObjectVisitor {
|
||
|
[ManualRefCounts]
|
||
|
internal override unsafe UIntPtr Visit(Object obj) {
|
||
|
VTable vtable = obj.vtable;
|
||
|
UIntPtr size =
|
||
|
ObjectLayout.ObjectSize(Magic.addressOf(obj), vtable);
|
||
|
|
||
|
uint refState = obj.REF_STATE;
|
||
|
UIntPtr refCount = (UIntPtr)(refState & RSMasks.refCount);
|
||
|
if ((refState & RSMasks.countingFlag) != 0 &&
|
||
|
refCount > 0) {
|
||
|
UIntPtr count = getBackupRefcount(obj);
|
||
|
if (count == 0) {
|
||
|
UIntPtr objAddr = Magic.addressOf(obj);
|
||
|
dfsMarker.Visit(&objAddr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private class DFSMarker : NonNullReferenceVisitor {
|
||
|
internal void Initialize() {
|
||
|
this.time = UIntPtr.Zero;
|
||
|
}
|
||
|
|
||
|
[ManualRefCounts]
|
||
|
internal override unsafe void Visit(UIntPtr* loc) {
|
||
|
UIntPtr objAddr = *loc;
|
||
|
|
||
|
UIntPtr page = PageTable.Page(objAddr);
|
||
|
if (!PageTable.IsGcPage(page)) {
|
||
|
PageType pageType = PageTable.Type(page);
|
||
|
VTable.Assert(pageType == PageType.NonGC ||
|
||
|
pageType == PageType.Stack,
|
||
|
@"pageType == PageType.NonGC ||
|
||
|
pageType == PageType.Stack");
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Object obj = Magic.fromAddress(objAddr);
|
||
|
if (obj.GcMark((UIntPtr)1)) {
|
||
|
this.time = this.time+1;
|
||
|
setDfsDiscoveryTime(obj, this.time);
|
||
|
|
||
|
UIntPtr vtableAddr = Magic.addressOf(obj.vtable);
|
||
|
this.Visit(&vtableAddr);
|
||
|
this.VisitReferenceFields(obj);
|
||
|
|
||
|
this.time = this.time+1;
|
||
|
setDfsFinishingTime(obj, this.time);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private UIntPtr time;
|
||
|
}
|
||
|
|
||
|
private class LeakedCycleClosure : ObjectVisitor {
|
||
|
[ManualRefCounts]
|
||
|
internal override unsafe UIntPtr Visit(Object obj) {
|
||
|
VTable vtable = obj.vtable;
|
||
|
UIntPtr size =
|
||
|
ObjectLayout.ObjectSize(Magic.addressOf(obj), vtable);
|
||
|
|
||
|
uint refState = obj.REF_STATE;
|
||
|
UIntPtr refCount = (UIntPtr)(refState & RSMasks.refCount);
|
||
|
if ((refState & RSMasks.countingFlag) != 0 &&
|
||
|
refCount > 0) {
|
||
|
UIntPtr count = getBackupRefcount(obj);
|
||
|
if (count == 0) {
|
||
|
UIntPtr dTime = getDfsDiscoveryTime(obj);
|
||
|
UIntPtr fTime = getDfsFinishingTime(obj);
|
||
|
cycleClosure.Initialize(dTime, fTime);
|
||
|
cycleClosure.VisitReferenceFields(obj);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private class CycleClosure : NonNullReferenceVisitor {
|
||
|
internal void Initialize(UIntPtr dTime, UIntPtr fTime) {
|
||
|
this.predDiscoveryTime = dTime;
|
||
|
this.predFinishingTime = fTime;
|
||
|
}
|
||
|
|
||
|
[ManualRefCounts]
|
||
|
internal override unsafe void Visit(UIntPtr* loc) {
|
||
|
UIntPtr objAddr = *loc;
|
||
|
|
||
|
UIntPtr page = PageTable.Page(objAddr);
|
||
|
if (!PageTable.IsGcPage(page)) {
|
||
|
PageType pageType = PageTable.Type(page);
|
||
|
VTable.Assert(pageType == PageType.NonGC ||
|
||
|
pageType == PageType.Stack,
|
||
|
@"pageType == PageType.NonGC ||
|
||
|
pageType == PageType.Stack");
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Object obj = Magic.fromAddress(objAddr);
|
||
|
UIntPtr dTime = getDfsDiscoveryTime(obj);
|
||
|
UIntPtr fTime = getDfsFinishingTime(obj);
|
||
|
VTable.Assert(this.predDiscoveryTime > UIntPtr.Zero &&
|
||
|
this.predFinishingTime > UIntPtr.Zero &&
|
||
|
dTime > UIntPtr.Zero &&
|
||
|
fTime > UIntPtr.Zero,
|
||
|
@"this.predDiscoveryTime > UIntPtr.Zero &&
|
||
|
this.predFinishingTime > UIntPtr.Zero &&
|
||
|
dTime > UIntPtr.Zero &&
|
||
|
fTime > UIntPtr.Zero");
|
||
|
|
||
|
if (dTime < this.predDiscoveryTime &&
|
||
|
this.predDiscoveryTime < this.predFinishingTime &&
|
||
|
this.predFinishingTime < fTime) {
|
||
|
// A back edge is incident on this node;
|
||
|
// therefore, the node is part of a cycle.
|
||
|
backupRefCount.Visit(&objAddr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private UIntPtr predDiscoveryTime;
|
||
|
private UIntPtr predFinishingTime;
|
||
|
}
|
||
|
|
||
|
private class ResetTraversal : ObjectVisitor {
|
||
|
[ManualRefCounts]
|
||
|
internal override unsafe UIntPtr Visit(Object obj) {
|
||
|
obj.GcMark(UIntPtr.Zero);
|
||
|
VTable vtable = obj.vtable;
|
||
|
UIntPtr size =
|
||
|
ObjectLayout.ObjectSize(Magic.addressOf(obj), vtable);
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private class BFSMarker : NonNullReferenceVisitor {
|
||
|
internal void Initialize(bool isVisited) {
|
||
|
this.isVisited = (UIntPtr)(isVisited ? 1 : 0);
|
||
|
}
|
||
|
|
||
|
[ManualRefCounts]
|
||
|
internal void Traverse(Object obj) {
|
||
|
this.VisitReferenceFields(obj);
|
||
|
while (!this.workList.IsEmpty) {
|
||
|
UIntPtr objAddr = this.workList.Read();
|
||
|
obj = Magic.fromAddress(objAddr);
|
||
|
this.VisitReferenceFields(obj);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[ManualRefCounts]
|
||
|
internal override unsafe void Visit(UIntPtr* loc) {
|
||
|
UIntPtr addr = *loc;
|
||
|
|
||
|
UIntPtr page = PageTable.Page(addr);
|
||
|
if (!PageTable.IsGcPage(page)) {
|
||
|
PageType pageType = PageTable.Type(page);
|
||
|
VTable.Assert(pageType == PageType.NonGC ||
|
||
|
pageType == PageType.Stack,
|
||
|
@"pageType == PageType.NonGC ||
|
||
|
pageType == PageType.Stack");
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
UIntPtr objAddr = SegregatedFreeList.Find(addr);
|
||
|
Object obj = Magic.fromAddress(objAddr);
|
||
|
if (obj.GcMark(this.isVisited)) {
|
||
|
this.workList.Write(objAddr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private UIntPtr isVisited;
|
||
|
private UIntPtrQueue workList;
|
||
|
}
|
||
|
|
||
|
private class LeakedRoots : ObjectVisitor {
|
||
|
internal void Initialize() {
|
||
|
bfsMarker.Initialize(true);
|
||
|
}
|
||
|
|
||
|
[ManualRefCounts]
|
||
|
internal override unsafe UIntPtr Visit(Object obj) {
|
||
|
VTable vtable = obj.vtable;
|
||
|
UIntPtr size =
|
||
|
ObjectLayout.ObjectSize(Magic.addressOf(obj), vtable);
|
||
|
|
||
|
uint refState = obj.REF_STATE;
|
||
|
UIntPtr refCount = (UIntPtr)(refState & RSMasks.refCount);
|
||
|
if ((refState & RSMasks.countingFlag) != 0 &&
|
||
|
refCount > 0) {
|
||
|
UIntPtr count = getBackupRefcount(obj);
|
||
|
if (count == 0 && obj.GcMark() == UIntPtr.Zero) {
|
||
|
bfsMarker.Traverse(obj);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private class LeakedRootsCounter : ObjectVisitor {
|
||
|
internal uint Total;
|
||
|
|
||
|
internal void Initialize() {
|
||
|
this.Total = 0;
|
||
|
}
|
||
|
|
||
|
[ManualRefCounts]
|
||
|
internal override unsafe UIntPtr Visit(Object obj) {
|
||
|
VTable vtable = obj.vtable;
|
||
|
UIntPtr size =
|
||
|
ObjectLayout.ObjectSize(Magic.addressOf(obj),
|
||
|
vtable);
|
||
|
|
||
|
uint refState = obj.REF_STATE;
|
||
|
UIntPtr refCount = (UIntPtr)(refState & RSMasks.refCount);
|
||
|
if ((refState & RSMasks.countingFlag) != 0 &&
|
||
|
refCount > 0) {
|
||
|
UIntPtr count = getBackupRefcount(obj);
|
||
|
if (count == 0 && obj.GcMark() == UIntPtr.Zero) {
|
||
|
this.Total++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
private static NonNullReferenceVisitor incrementer;
|
||
|
private static NonNullReferenceVisitor decrementer;
|
||
|
|
||
|
}
|
||
|
|
||
|
|