// // 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 abstract class SingleThreadedRCCollector : RCCollector { [PreInitRefCounts] public new static void Initialize() { RCCollector.Initialize(); if (VerificationMode) { backupInit = (BackupInitializer)BootstrapMemory. Allocate(typeof(BackupInitializer)); backupRefCount = (BackupRefCount)BootstrapMemory. Allocate(typeof(BackupRefCount)); incrementBackupRefCount = (IncrementBackupRefCount)BootstrapMemory. Allocate(typeof(IncrementBackupRefCount)); rootsScanner = (RootsScanner)BootstrapMemory. Allocate(typeof(RootsScanner)); resetRoots = (ResetRoots)BootstrapMemory. Allocate(typeof(ResetRoots)); resetTraversal = (ResetTraversal)BootstrapMemory. Allocate(typeof(ResetTraversal)); leakAccumulator = (LeakAccumulator)BootstrapMemory. Allocate(typeof(LeakAccumulator)); leakedNodesDFS = (LeakedNodesDFS)BootstrapMemory. Allocate(typeof(LeakedNodesDFS)); leakedCycleClosure = (LeakedCycleClosure)BootstrapMemory. Allocate(typeof(LeakedCycleClosure)); dfsMarker = (DFSMarker)BootstrapMemory. Allocate(typeof(DFSMarker)); cycleClosure = (CycleClosure)BootstrapMemory. Allocate(typeof(CycleClosure)); bfsMarker = (BFSMarker)BootstrapMemory. Allocate(typeof(BFSMarker)); leakedRoots = (LeakedRoots)BootstrapMemory. Allocate(typeof(LeakedRoots)); leakedRootsCounter = (LeakedRootsCounter)BootstrapMemory. Allocate(typeof(LeakedRootsCounter)); } } internal override bool IsOnTheFlyCollector { get { return false; } } internal override void CheckForNeededGCWork (Thread currentThread) { } internal override int GetGeneration(Object obj) { return MinGeneration; } internal override int MinGeneration { get { return (int)PageType.Owner0; } } internal override int MaxGeneration { get { return (int)PageType.Owner0; } } internal override int CollectionGeneration(int gen) { return MinGeneration; } internal override void NewThreadNotification(Thread newThread, bool initial) { base.NewThreadNotification(newThread, initial); SegregatedFreeList.NewThreadNotification(newThread, initial); } internal override void DeadThreadNotification(Thread deadThread) { MultiUseWord.CollectFromThread(deadThread); SegregatedFreeList.DeadThreadNotification(deadThread); base.DeadThreadNotification(deadThread); } [NoInline] [ManualRefCounts] protected void preVerifyHeap(bool beforeCollection) { VTable.Assert(RCCollector.VerificationMode, @"RCCollector.VerificationMode"); // Ensure the integrity of the delayed deallocation list. deallocationListChecker(); } [NoInline] [ManualRefCounts] protected void postVerifyHeap(bool beforeCollection) { VTable.Assert(RCCollector.VerificationMode, @"RCCollector.VerificationMode"); SegregatedFreeList.RecycleGlobalPages(); SegregatedFreeList.CommitFreedData(); GC.newBytesSinceGC = UIntPtr.Zero; // Initialize the "backup" reference count. SegregatedFreeList.VisitAllObjects(backupInit); // Count all references and managed pointers. rootsScanner.Initialize(backupRefCount); CallStack.ScanStacks(rootsScanner, rootsScanner); Thread.VisitBootstrapData(rootsScanner); StaticData.ScanStaticData(rootsScanner); MultiUseWord.VisitStrongRefs(rootsScanner, false); CallStack.ScanStacks(resetRoots, resetRoots); Thread.VisitBootstrapData(resetRoots); StaticData.ScanStaticData(resetRoots); SegregatedFreeList.VisitAllObjects(resetTraversal); // Actual leaks (refCount > 0 and backup refCount = 0). leakAccumulator.Initialize(); SegregatedFreeList.VisitAllObjects(leakAccumulator); VTable.DebugPrint("Leaked storage: "); VTable.DebugPrint((int)leakAccumulator.Size); VTable.DebugPrint("B"); if (VerifyLeakedCycles) { // Find leaked data that *should* have been reclaimed. // (If L is the set of all leaked nodes, and L' the // transitive closure of leaked cycles, then L-L' is // the set of nodes that should have been captured // by a pure RC collector.) SegregatedFreeList.VisitAllObjects(leakedNodesDFS); SegregatedFreeList.VisitAllObjects(resetTraversal); SegregatedFreeList.VisitAllObjects(leakedCycleClosure); SegregatedFreeList.VisitAllObjects(resetTraversal); leakAccumulator.Initialize(); SegregatedFreeList.VisitAllObjects(leakAccumulator); VTable.DebugPrint(" ("); VTable.DebugPrint((int)leakAccumulator.Size); VTable.DebugPrint("B acyclic)"); } // Find the roots of leaked data. leakedRoots.Initialize(); SegregatedFreeList.VisitAllObjects(leakedRoots); leakedRootsCounter.Initialize(); SegregatedFreeList.VisitAllObjects(leakedRootsCounter); SegregatedFreeList.VisitAllObjects(resetTraversal); VTable.DebugPrint("; leaked heap roots: "); VTable.DebugPrint((int)leakedRootsCounter.Total); VTable.DebugPrint("\n"); } [Inline] [ManualRefCounts] [RequiredByBartok] protected static void UpdateReferentRefCounts (UIntPtr objAddr, VTable vtable, NonNullReferenceVisitor updater) { updater.VisitReferenceFields(objAddr, vtable); } [Inline] [ManualRefCounts] [RequiredByBartok] protected static unsafe void UpdateReferentRefCounts (UIntPtr objAddr, VTable vtable, int start, int span, NonNullReferenceVisitor updater) { uint pointerTracking = (uint)vtable.pointerTrackingMask; uint objTag = pointerTracking & 0xf; switch (objTag) { case ObjectLayout.PTR_VECTOR_TAG: { uint length = *(uint*)(objAddr+PostHeader.Size); VTable.Assert(span <= length, @"span <= length"); UIntPtr* elemAddress = (UIntPtr*)(objAddr+ vtable.baseLength-PreHeader.Size)+start; for (int i = 0; i < span; i++, elemAddress++) { updater.Visit(elemAddress); } break; } case ObjectLayout.OTHER_VECTOR_TAG: { uint length = *(uint*)(objAddr+PostHeader.Size); VTable.Assert(span <= length, @"span <= length"); if (vtable.arrayOf == StructuralType.Struct) { int elemSize = vtable.arrayElementSize; UIntPtr elemAddress = objAddr+vtable.baseLength- PreHeader.Size-PostHeader.Size+elemSize*start; VTable elemVTable = vtable.arrayElementClass; for (int i = 0; i < span; i++, elemAddress += elemSize) { updater.VisitReferenceFields(elemAddress, elemVTable); } } break; } case ObjectLayout.PTR_ARRAY_TAG: { uint length = *(uint*)(objAddr+PostHeader.Size+sizeof(uint)); VTable.Assert(span <= length, @"span <= length"); UIntPtr* elemAddress = (UIntPtr*) (objAddr+vtable.baseLength-PreHeader.Size)+start; for (int i = 0; i < span; i++, elemAddress++) { updater.Visit(elemAddress); } break; } case ObjectLayout.OTHER_ARRAY_TAG: { uint length = *(uint*)(objAddr+PostHeader.Size+sizeof(uint)); VTable.Assert(span <= length, @"span <= length"); if (vtable.arrayOf == StructuralType.Struct) { int elemSize = vtable.arrayElementSize; UIntPtr elemAddress = objAddr+vtable.baseLength- PreHeader.Size-PostHeader.Size+elemSize*start; VTable elemVTable = vtable.arrayElementClass; for (int i = 0; i < span; i++, elemAddress += elemSize) { updater.VisitReferenceFields(elemAddress, elemVTable); } } break; } case ObjectLayout.STRING_TAG: { break; } default: { VTable.NotReached("An unsupported tag found!"); break; } } } [NoInline] [ManualRefCounts] protected static void deallocationListChecker() { // Check for nonzero reference counts and for // loops in the delayed deallocation list. for (Object block = delayedDeallocationList; block != null; block = getNextLink(block)) { UIntPtr objAddr = Magic.addressOf(block); UIntPtr page = PageTable.Page(objAddr); if (!PageTable.IsGcPage(page)) { VTable.DebugPrint("Non-GC memory for freeing!\n"); VTable.DebugBreak(); } uint refState = block.REF_STATE; if ((refState & RSMasks.refCount) != 0) { VTable.DebugPrint("Non-zero reference count!\n"); VTable.DebugBreak(); } block.REF_STATE = refState+1; } // Make another pass to reset reference counts. for (Object block = delayedDeallocationList; block != null; block = getNextLink(block)) { block.REF_STATE--; } } [NoInline] [ManualRefCounts] protected static void purgeDeallocationList (NonNullReferenceVisitor decrementer) { while (beingDeallocatedBlock != null || delayedDeallocationList != null) { deallocateObjects(decrementer); } } [Inline] [ManualRefCounts] protected static void deallocateLazily(Object obj) { setNextLink(obj, delayedDeallocationList); delayedDeallocationList = obj; delayedDeallocationLength++; } [Inline] [ManualRefCounts] protected static void deallocateObjects (NonNullReferenceVisitor decrementer) { int startTicks = 0; bool enableGCTiming = VTable.enableGCTiming; if (enableGCTiming) { VTable.enableGCTiming = false; startTicks = Environment.TickCount; } if (VTable.enableGCWatermarks) { MemoryAccounting.RecordHeapWatermarks(); } // Set up a block to deallocate, if one doesn't exist. if (beingDeallocatedBlock == null && delayedDeallocationList != null) { beingDeallocatedBlock = delayedDeallocationList; delayedDeallocationList = getNextLink(delayedDeallocationList); delayedDeallocationLength--; UIntPtr objAddr = Magic.addressOf(beingDeallocatedBlock); VTable vtable = beingDeallocatedBlock.vtable; initIncrementalDecrement(objAddr, vtable); } // Perform up to a constant number of work chunks on the // block being deallocated. A "work chunk" is either // decrementing up to a fixed number of references held in // an object, decrementing up to a fixed number of slots // if the object is an array, or reclaiming the object // after all decrements on its internal contents are done. for (uint workDone = 0; beingDeallocatedBlock != null && workDone < deallocationSpan; workDone++) { // Continue work on block. UIntPtr objAddr = Magic.addressOf(beingDeallocatedBlock); #if DEBUG UIntPtr page = PageTable.Page(objAddr); VTable.Assert(PageTable.IsGcPage(page), @"PageTable.IsGcPage(page)"); #endif // DEBUG VTable vtable = beingDeallocatedBlock.vtable; if (incrementalDecrement(objAddr, vtable, decrementer) != 0) { continue; } // All decrements on contained references are over. Object obj = beingDeallocatedBlock; VTable.Assert((obj.REF_STATE & RSMasks.refCount) == 0, @"(obj.REF_STATE & RSMasks.refCount) == 0"); #if DEBUG PLCLink* plcLinkAddr = GetPLCLink(obj); VTable.Assert(plcLinkAddr == null, @"plcLinkAddr == null"); #endif // DEBUG SegregatedFreeList.Free(obj); // Set up block to work on next. beingDeallocatedBlock = delayedDeallocationList; if (delayedDeallocationList != null) { delayedDeallocationList = getNextLink(delayedDeallocationList); delayedDeallocationLength--; objAddr = Magic.addressOf(beingDeallocatedBlock); vtable = beingDeallocatedBlock.vtable; initIncrementalDecrement(objAddr, vtable); } } SegregatedFreeList.RecycleGlobalPages(); SegregatedFreeList.CommitFreedData(); GC.newBytesSinceGC = UIntPtr.Zero; if (enableGCTiming) { int elapsedTicks = Environment.TickCount - startTicks; System.GC.gcTotalTime += elapsedTicks; if (System.GC.maxPauseTime < elapsedTicks) { System.GC.maxPauseTime = elapsedTicks; } System.GC.pauseCount++; VTable.enableGCTiming = true; } } [Inline] [ManualRefCounts] protected static unsafe uint incrementalDecrement (UIntPtr objAddr, VTable vtable, NonNullReferenceVisitor decrementer) { uint pointerTracking = (uint)vtable.pointerTrackingMask; uint objTag = pointerTracking & 0xf; uint workDone = 0; switch (objTag) { case ObjectLayout.SPARSE_TAG: { UIntPtr* sparseObject = (UIntPtr*)objAddr; uint lastPointerTracking = getLastTracked(objAddr); for (pointerTracking = lastPointerTracking; pointerTracking != 0 && workDone < decrementSpan; workDone++) { uint index = pointerTracking & 0xf; pointerTracking >>= 4; UIntPtr* loc = sparseObject+(int)index; UIntPtr addr = *loc; if (addr != UIntPtr.Zero) { decrementer.Visit(addr); } } setLastTracked(objAddr, pointerTracking); break; } case ObjectLayout.DENSE_TAG: { UIntPtr* denseObject = (UIntPtr*) (objAddr+PostHeader.Size); int lastIndex = unchecked((int)getLastTracked(objAddr)); pointerTracking >>= lastIndex+4; for (denseObject += lastIndex; pointerTracking != 0 && workDone < decrementSpan; workDone++, lastIndex++, denseObject++) { if ((pointerTracking & 0x1) != 0 && *denseObject != UIntPtr.Zero) { decrementer.Visit(denseObject); } pointerTracking >>= 1; } setLastTracked(objAddr, unchecked((uint)lastIndex)); break; } case ObjectLayout.PTR_VECTOR_TAG: { uint length = *(uint*)(objAddr+PostHeader.Size); UIntPtr* elemAddress = (UIntPtr*) (objAddr+vtable.baseLength-PreHeader.Size); uint lastIndex = getLastTracked(objAddr); for (elemAddress += lastIndex; lastIndex < length && workDone < decrementSpan; workDone++, lastIndex++, elemAddress++) { if (*elemAddress != UIntPtr.Zero) { decrementer.Visit(elemAddress); } } setLastTracked(objAddr, lastIndex); break; } case ObjectLayout.OTHER_VECTOR_TAG: { if (vtable.arrayOf == StructuralType.Struct) { uint length = *(uint*)(objAddr+PostHeader.Size); UIntPtr elemAddress = objAddr+vtable.baseLength- PreHeader.Size-PostHeader.Size; VTable elemVTable = vtable.arrayElementClass; int elemSize = vtable.arrayElementSize; uint lastIndex = getLastTracked(objAddr); for (elemAddress += (UIntPtr)(elemSize*lastIndex); lastIndex < length && workDone < decrementSpan; workDone++, lastIndex++, elemAddress += elemSize) { decrementer.VisitReferenceFields(elemAddress, elemVTable); } setLastTracked(objAddr, lastIndex); } break; } case ObjectLayout.PTR_ARRAY_TAG: { uint length = *(uint*)(objAddr+PostHeader.Size+sizeof(uint)); UIntPtr* elemAddress = (UIntPtr*) (objAddr+vtable.baseLength-PreHeader.Size); uint lastIndex = getLastTracked(objAddr); for (elemAddress += lastIndex; lastIndex < length && workDone < decrementSpan; workDone++, lastIndex++, elemAddress++) { if (*elemAddress != UIntPtr.Zero) { decrementer.Visit(elemAddress); } } setLastTracked(objAddr, lastIndex); break; } case ObjectLayout.OTHER_ARRAY_TAG: { if (vtable.arrayOf == StructuralType.Struct) { uint length = *(uint*)(objAddr+ PostHeader.Size+sizeof(uint)); UIntPtr elemAddress = objAddr+vtable.baseLength- PreHeader.Size-PostHeader.Size; VTable elemVTable = vtable.arrayElementClass; int elemSize = vtable.arrayElementSize; uint lastIndex = getLastTracked(objAddr); for (elemAddress += (UIntPtr)(elemSize*lastIndex); lastIndex < length && workDone < decrementSpan; workDone++, lastIndex++, elemAddress += elemSize) { decrementer.VisitReferenceFields(elemAddress, elemVTable); } setLastTracked(objAddr, lastIndex); } break; } case ObjectLayout.STRING_TAG: { break; } default: { VTable.Assert((objTag & 0x1) == 0, @"(objTag & 0x1) == 0"); UIntPtr* largeObject = (UIntPtr*)objAddr; int* pointerDescription = (int*)vtable.pointerTrackingMask; int lastCount = unchecked((int)getLastTracked(objAddr)); for (int count = *pointerDescription; lastCount <= count && workDone < decrementSpan; workDone++, lastCount++) { UIntPtr* loc = largeObject+ *(pointerDescription+lastCount); if (*loc != UIntPtr.Zero) { decrementer.Visit(loc); } } setLastTracked(objAddr, unchecked((uint)lastCount)); break; } } return workDone; } [ManualRefCounts] protected static unsafe void initIncrementalDecrement (UIntPtr objAddr, VTable vtable) { uint pointerTracking = (uint)vtable.pointerTrackingMask; uint objTag = pointerTracking & 0xf; switch (objTag) { case ObjectLayout.SPARSE_TAG: { setLastTracked(objAddr, pointerTracking >> 4); break; } case ObjectLayout.DENSE_TAG: { setLastTracked(objAddr, 0); break; } case ObjectLayout.PTR_VECTOR_TAG: case ObjectLayout.OTHER_VECTOR_TAG: case ObjectLayout.PTR_ARRAY_TAG: case ObjectLayout.OTHER_ARRAY_TAG: { setLastTracked(objAddr, 0); break; } case ObjectLayout.STRING_TAG : { break; } default: { setLastTracked(objAddr, 1); break; } } } [Inline] [ManualRefCounts] protected static Object getNextLink(Object obj) { return Magic.fromAddress(obj.preHeader.muw.value); } [Inline] [ManualRefCounts] protected static void setNextLink(Object obj, Object next) { obj.preHeader.muw.value = Magic.addressOf(next); } [Inline] [ManualRefCounts] protected static uint getLastTracked(UIntPtr objAddr) { Object obj = Magic.fromAddress(objAddr); return (uint)obj.preHeader.muw.value; } [Inline] [ManualRefCounts] protected static void setLastTracked(UIntPtr objAddr, uint last) { Object obj = Magic.fromAddress(objAddr); obj.preHeader.muw.value = (UIntPtr)last; } [Inline] [ManualRefCounts] protected static UIntPtr getBackupRefcount(Object obj) { return ((RCGCVerificationObject)obj).preHeader. backupRefcount; } [Inline] [ManualRefCounts] protected static void setBackupRefcount(Object obj, UIntPtr count) { ((RCGCVerificationObject)obj).preHeader.backupRefcount = count; } [Inline] [ManualRefCounts] protected static UIntPtr getDfsDiscoveryTime(Object obj) { return ((RCGCVerificationObject)obj).preHeader. dfsDiscoveryTime; } [Inline] [ManualRefCounts] protected static void setDfsDiscoveryTime(Object obj, UIntPtr time) { ((RCGCVerificationObject)obj).preHeader.dfsDiscoveryTime = time; } [Inline] [ManualRefCounts] protected static UIntPtr getDfsFinishingTime(Object obj) { return ((RCGCVerificationObject)obj).preHeader. dfsFinishingTime; } [Inline] [ManualRefCounts] protected static void setDfsFinishingTime(Object obj, UIntPtr time) { ((RCGCVerificationObject)obj).preHeader.dfsFinishingTime = time; } protected 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); } protected 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 BackupInitializer : ObjectVisitor { [ManualRefCounts] internal override UIntPtr Visit(Object obj) { setBackupRefcount(obj, UIntPtr.Zero); return ObjectLayout.ObjectSize(Magic.addressOf(obj), 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 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 Object delayedDeallocationList; private static Object beingDeallocatedBlock; private static uint delayedDeallocationLength; private const uint collectionTrigger = 1 << 15; private const uint deallocationSpan = 1 << 20; private const uint decrementSpan = 1 << 8; // Used only in verification mode. private static BackupInitializer backupInit; private static BackupRefCount backupRefCount; private static IncrementBackupRefCount incrementBackupRefCount; private static RootsScanner rootsScanner; private static NonNullReferenceVisitor resetRoots; private static ObjectVisitor resetTraversal; private static LeakAccumulator leakAccumulator; private static LeakedNodesDFS leakedNodesDFS; private static LeakedCycleClosure leakedCycleClosure; private static CycleClosure cycleClosure; private static DFSMarker dfsMarker; private static BFSMarker bfsMarker; private static LeakedRootsCounter leakedRootsCounter; private static LeakedRoots leakedRoots; } }