1123 lines
42 KiB
C#
1123 lines
42 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 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;
|
|
}
|
|
}
|
|
|
|
|