702 lines
25 KiB
C#
702 lines
25 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 the Bartok Depot and propagated to the Singularity Depot. */
|
|
/*******************************************************************/
|
|
|
|
|
|
namespace System
|
|
{
|
|
using Microsoft.Bartok.Runtime;
|
|
using System.GCs;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading;
|
|
|
|
#if SINGULARITY
|
|
using Microsoft.Singularity;
|
|
#endif
|
|
|
|
// The GC has only static members and doesn't require the serializable
|
|
// keyword.
|
|
[CCtorIsRunDuringStartup]
|
|
[RequiredByBartok]
|
|
[AccessedByRuntime("referenced in {hal,brt}{asm,forgc}.asm")]
|
|
#if SINGULARITY
|
|
[CLSCompliant(false)]
|
|
#endif
|
|
public sealed class GC
|
|
{
|
|
// This is a compiler intrinsic whose value is controlled by
|
|
// /StageControl.HeapSizeConfigurable.
|
|
internal static extern bool HeapSizeConfigurable {
|
|
[Intrinsic]
|
|
get;
|
|
}
|
|
// The *initial* maximum heap size, used if
|
|
// HeapSizeConfigurable is true.
|
|
internal static int MaxHeapPages = 96;
|
|
|
|
// Bartok runtime "magic" function
|
|
// It saves the callee-save registers in a transition record and
|
|
// calls System.GC.CollectBody(thread, generation)
|
|
[ManualRefCounts]
|
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
|
[GCAnnotation(GCOption.GCFRIEND)]
|
|
[StackBound(128)]
|
|
private static extern void CollectBodyTransition(Thread thread,
|
|
int generation);
|
|
#if SINGULARITY_KERNEL
|
|
internal static uint perfCounter = 6;
|
|
#else
|
|
internal static uint perfCounter = 5;
|
|
#endif
|
|
|
|
[TrustedNonNull]
|
|
internal static Collector installedGC;
|
|
|
|
private static bool isProfiling;
|
|
|
|
// Flag to indicate to the collector that it should perform a
|
|
// retrace in a stop-the-world mode after finishing a mostly
|
|
// normal trace. This allows us to measure the cost of various
|
|
// barriers by disabling them. Currently only recognized by CONCMS.
|
|
internal static bool enableSTWRetrace;
|
|
|
|
[RequiredByBartok]
|
|
private static Object dummyGlobal; // Used by KeepAlive
|
|
|
|
[AccessedByRuntime("referenced in halasm.asm/brtasm.asm")]
|
|
internal static int allocationGCInhibitCount = 0;
|
|
|
|
[Intrinsic]
|
|
internal static GCType gcType;
|
|
|
|
[Intrinsic]
|
|
internal static WBType wbType;
|
|
|
|
[Intrinsic]
|
|
internal static RemSetType remsetType;
|
|
|
|
[Intrinsic]
|
|
internal static CopyScanType copyscanType;
|
|
|
|
#if !SINGULARITY // Needed before Bartok runtime is initialized
|
|
[NoStackLinkCheck]
|
|
#endif
|
|
[PreInitRefCounts]
|
|
internal static void ConstructHeap() {
|
|
PageTable.Initialize();
|
|
|
|
MemoryManager.Initialize();
|
|
#if OS_WINCE
|
|
UIntPtr heap_commit_size = new UIntPtr(1 << 16);
|
|
#elif SINGULARITY
|
|
UIntPtr heap_commit_size = new UIntPtr(1 << 16);
|
|
#else
|
|
UIntPtr heap_commit_size = new UIntPtr(1 << 20);
|
|
#endif
|
|
UIntPtr os_commit_size = MemoryManager.OperatingSystemCommitSize;
|
|
VTable.Assert(os_commit_size > UIntPtr.Zero);
|
|
VTable.Assert(heap_commit_size >= os_commit_size);
|
|
UIntPtr bootstrapSize;
|
|
if (UIntPtr.Size == 8) {
|
|
if (gcType == GCType.ConcurrentMSCollector) {
|
|
// increase bootstrap size so that
|
|
// the concurrent mark sweep collector will run on
|
|
// 64-bit Windows
|
|
bootstrapSize = (UIntPtr) 1<<16;
|
|
} else {
|
|
bootstrapSize = (UIntPtr) 1<<15;
|
|
}
|
|
} else {
|
|
bootstrapSize = (UIntPtr) 1<<14;
|
|
}
|
|
if (bootstrapSize < os_commit_size) {
|
|
bootstrapSize = os_commit_size;
|
|
}
|
|
BootstrapMemory.Initialize(bootstrapSize);
|
|
StaticData.Initialize();
|
|
PageManager.Initialize(os_commit_size, heap_commit_size);
|
|
CallStack.Initialize();
|
|
}
|
|
|
|
// Called after the GC is up, but before multi-threading is enabled.
|
|
internal static void FinishInitializeThread()
|
|
{
|
|
PageManager.FinishInitializeThread();
|
|
}
|
|
|
|
// NB: This is called from VTable.Initialize()
|
|
[PreInitRefCounts]
|
|
static GC() // Class Constructor (cctor)
|
|
{
|
|
GC.Initialize();
|
|
switch(gcType) {
|
|
#if !SINGULARITY || ADAPTIVE_COPYING_COLLECTOR
|
|
case GCType.AdaptiveCopyingCollector: {
|
|
AdaptiveCopyingCollector.Initialize();
|
|
GC.installedGC = AdaptiveCopyingCollector.instance;
|
|
break;
|
|
}
|
|
#endif
|
|
#if !SINGULARITY || MARK_SWEEP_COLLECTOR
|
|
case GCType.MarkSweepCollector: {
|
|
MarkSweepCollector.Initialize();
|
|
GC.installedGC = MarkSweepCollector.instance;
|
|
break;
|
|
}
|
|
#endif
|
|
#if !SINGULARITY || TABLE_MARK_SWEEP_COLLECTOR
|
|
case GCType.TableMarkSweepCollector: {
|
|
SimpleMarkSweepCollector.Initialize();
|
|
GC.installedGC = SimpleMarkSweepCollector.instance;
|
|
break;
|
|
}
|
|
#endif
|
|
#if !SINGULARITY || SEMISPACE_COLLECTOR
|
|
case GCType.SemispaceCollector: {
|
|
SemispaceCollector.Initialize();
|
|
GC.installedGC = SemispaceCollector.instance;
|
|
break;
|
|
}
|
|
#endif
|
|
#if !SINGULARITY || SLIDING_COLLECTOR
|
|
case GCType.SlidingCollector: {
|
|
SlidingCollector.Initialize();
|
|
GC.installedGC = SlidingCollector.instance;
|
|
break;
|
|
}
|
|
#endif
|
|
#if !SINGULARITY || CONCURRENT_MS_COLLECTOR
|
|
case GCType.ConcurrentMSCollector: {
|
|
ConcurrentMSCollector.Initialize();
|
|
GC.installedGC = ConcurrentMSCollector.instance;
|
|
break;
|
|
}
|
|
#endif
|
|
#if !SINGULARITY || ATOMIC_RC_COLLECTOR
|
|
case GCType.AtomicRCCollector: {
|
|
AtomicRCCollector.Initialize();
|
|
GC.installedGC = AtomicRCCollector.instance;
|
|
break;
|
|
}
|
|
#endif
|
|
#if !SINGULARITY
|
|
case GCType.ReferenceCountingCollector: {
|
|
ReferenceCountingCollector.Initialize();
|
|
GC.installedGC = ReferenceCountingCollector.instance;
|
|
break;
|
|
}
|
|
#endif
|
|
#if !SINGULARITY
|
|
case GCType.DeferredReferenceCountingCollector: {
|
|
DeferredReferenceCountingCollector.Initialize();
|
|
GC.installedGC = DeferredReferenceCountingCollector.instance;
|
|
break;
|
|
}
|
|
#endif
|
|
#if !SINGULARITY || NULL_COLLECTOR
|
|
case GCType.NullCollector: {
|
|
VTable.Assert(wbType == 0, "No need for a write barrier");
|
|
GC.installedGC =
|
|
(NullCollector)
|
|
BootstrapMemory.Allocate(typeof(NullCollector));
|
|
break;
|
|
}
|
|
#endif
|
|
#if !SINGULARITY
|
|
case GCType.CoCoMSCollector: {
|
|
CoCoMSCollector.Initialize();
|
|
GC.installedGC = CoCoMSCollector.instance;
|
|
break;
|
|
}
|
|
#endif
|
|
default: {
|
|
VTable.NotReached("Unknown GC type: "+gcType);
|
|
break;
|
|
}
|
|
}
|
|
GC.installedGC.NewThreadNotification(Thread.initialThread, true);
|
|
GC.installedGC.ThreadStartNotification(Thread.initialThread.threadIndex);
|
|
}
|
|
|
|
[NoBarriers]
|
|
[PreInitRefCounts]
|
|
private static void Initialize()
|
|
{
|
|
VTable.Assert(GC.allocationGCInhibitCount == 0);
|
|
GC.allocationGCInhibitCount = 1;
|
|
Transitions.Initialize();
|
|
Barrier.Initialize();
|
|
}
|
|
|
|
#if !SINGULARITY
|
|
private static DateTime LogMessage(String message)
|
|
{
|
|
DateTime currentTime = System.DateTime.Now;
|
|
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
|
String hourString = currentTime.Hour.ToString();
|
|
if (hourString.Length == 1) {
|
|
sb.Append('0');
|
|
}
|
|
sb.Append(hourString);
|
|
sb.Append(':');
|
|
String minuteString = currentTime.Minute.ToString();
|
|
if (minuteString.Length == 1) {
|
|
sb.Append('0');
|
|
}
|
|
sb.Append(minuteString);
|
|
sb.Append(':');
|
|
String secondString = currentTime.Second.ToString();
|
|
if (secondString.Length == 1) {
|
|
sb.Append('0');
|
|
}
|
|
sb.Append(secondString);
|
|
sb.Append('.');
|
|
String milliString = currentTime.Millisecond.ToString();
|
|
if (milliString.Length < 3) {
|
|
sb.Append('0');
|
|
}
|
|
if (milliString.Length < 2) {
|
|
sb.Append('0');
|
|
}
|
|
sb.Append(milliString);
|
|
sb.Append(": ");
|
|
sb.Append(message);
|
|
Console.Out.WriteLine(sb.ToString());
|
|
return currentTime;
|
|
}
|
|
#endif
|
|
|
|
// This empty class allows us to easily spot the HeapCritialSection
|
|
// mutex when debugging.
|
|
private class HeapMonitor
|
|
{
|
|
}
|
|
|
|
internal static void CheckForNeededGCWork(Thread currentThread) {
|
|
installedGC.CheckForNeededGCWork(currentThread);
|
|
}
|
|
|
|
#if SINGULARITY
|
|
// This is a Singularity special not in the CLR
|
|
public static void Verify()
|
|
{
|
|
DebugStub.WriteLine("Calling VerifyHeap()");
|
|
bool oldGCVerify = VTable.enableGCVerify;
|
|
VTable.enableGCVerify = true;
|
|
Collect();
|
|
VTable.enableGCVerify = oldGCVerify;
|
|
DebugStub.WriteLine("Verification finished.");
|
|
}
|
|
|
|
public static void PerformanceCounters(out int collectorCount,
|
|
out long collectorMillis,
|
|
out long collectorBytes)
|
|
{
|
|
collectorCount = BaseCollector.gcTotalCount;
|
|
collectorMillis = BaseCollector.gcTotalTime;
|
|
collectorBytes = BaseCollector.gcTotalBytes;
|
|
}
|
|
#endif
|
|
|
|
// Garbage Collect all generations.
|
|
[ManualRefCounts]
|
|
public static void Collect()
|
|
{
|
|
installedGC.Collect(Thread.CurrentThread, MaxGeneration);
|
|
}
|
|
|
|
public static void Collect(int generation)
|
|
{
|
|
if (generation < 0) {
|
|
throw new ArgumentOutOfRangeException(
|
|
"generation",
|
|
"Argument should be positive!");
|
|
}
|
|
installedGC.Collect(Thread.CurrentThread, generation);
|
|
}
|
|
|
|
// This method is to be used by various runtime routines that
|
|
// need to trigger a collection. If there are different kinds
|
|
// of collection, most likely a minor collection will be
|
|
// performed.
|
|
internal static void InvokeCollection(Thread currentThread)
|
|
{
|
|
installedGC.Collect(currentThread,
|
|
(int)SpecialGeneration.InvokeCollection);
|
|
}
|
|
|
|
// This method is to be used by various runtime routines that
|
|
// need to trigger a collection. If there are different kinds
|
|
// of collections, most likely a major collection will be
|
|
// performed.
|
|
internal static void InvokeMajorCollection(Thread currentThread)
|
|
{
|
|
installedGC.Collect(currentThread,
|
|
(int)SpecialGeneration.InvokeMajorCollection);
|
|
}
|
|
|
|
// This method is to be used by various garbage collector routines
|
|
// that need to trigger a scanning for roots of the call stacks
|
|
// of the program threads. Calling this method is not meant
|
|
// to trigger a new garbage collection cycle.
|
|
internal static void InvokeStackScanOnly(Thread currentThread)
|
|
{
|
|
installedGC.Collect(currentThread,
|
|
(int)SpecialGeneration.InvokeStackScanOnly);
|
|
}
|
|
|
|
// This method is to be used by various garbage collector routines
|
|
// that need to trigger an update of all references on the call
|
|
// stacks of the program threads. Calling this method is not meant
|
|
// to trigger a new garbage collection cycle.
|
|
internal static void InvokeStackFixupOnly(Thread currentThread)
|
|
{
|
|
installedGC.Collect(currentThread,
|
|
(int)SpecialGeneration.InvokeStackFixupOnly);
|
|
}
|
|
|
|
internal static void CollectTransition(Thread currentThread,
|
|
int generation)
|
|
{
|
|
CollectBodyTransition(currentThread, generation);
|
|
}
|
|
|
|
// DO NOT REMOVE THE StackLinkCheck ATTRIBUTE FROM THIS
|
|
// FUNCTION!
|
|
//
|
|
// It is called from native code System.GC.CollectBodyTransition
|
|
// that only has an attribute for the amount of stack space that
|
|
// the native code requires.
|
|
[StackLinkCheck]
|
|
[ManualRefCounts]
|
|
[AccessedByRuntime("called from halforgc.asm/brtforgc.asm")]
|
|
private static unsafe Thread CollectBody(Thread currentThread,
|
|
int generation)
|
|
{
|
|
// NOTE: Refrain from creating any GC safe points in this
|
|
// method. That includes allocation and system calls,
|
|
// which in turn includes print statements. Putting a GC
|
|
// safe point before the call to the collector may cause
|
|
// infinite recursion. Putting a GC safe point after the
|
|
// call to the collector may cause recursion when other
|
|
// threads are triggering collections before the current
|
|
// thread exits this method. (Bjarne)
|
|
int currentThreadIndex = currentThread.threadIndex;
|
|
installedGC.CollectStoppable(currentThreadIndex, generation);
|
|
return Thread.threadTable[currentThreadIndex];
|
|
}
|
|
|
|
[RequiredByBartok]
|
|
[AccessedByRuntime("called from brtasm.asm")]
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
internal static Object AllocateObject(VTable vtable)
|
|
{
|
|
return AllocateObject(vtable, Thread.CurrentThread);
|
|
}
|
|
|
|
[RequiredByBartok]
|
|
[NoInline]
|
|
[CalledRarely]
|
|
[ManualRefCounts]
|
|
internal static Object AllocateObjectNoInline(VTable vtable)
|
|
{
|
|
return AllocateObject(vtable);
|
|
}
|
|
|
|
[RequiredByBartok]
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
internal static Object AllocateObject(VTable vtable,
|
|
Thread currentThread)
|
|
{
|
|
VTable.Deny(Transitions.UnderGCControl(currentThread.threadIndex));
|
|
return installedGC.AllocateObject(vtable, currentThread);
|
|
}
|
|
|
|
[RequiredByBartok]
|
|
[NoInline]
|
|
[CalledRarely]
|
|
[ManualRefCounts]
|
|
internal static Object AllocateObjectNoInline(VTable vtable,
|
|
Thread currentThread)
|
|
{
|
|
return AllocateObject(vtable, currentThread);
|
|
}
|
|
|
|
[RequiredByBartok]
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
internal static Object AllocateObject(VTable vtable,
|
|
UIntPtr numBytes,
|
|
uint baseAlignment,
|
|
Thread currentThread)
|
|
{
|
|
VTable.Deny(Transitions.UnderGCControl(currentThread.threadIndex));
|
|
return installedGC.AllocateObject(vtable,
|
|
numBytes,
|
|
baseAlignment,
|
|
currentThread);
|
|
}
|
|
|
|
// Currently only used by MDIL as the compiler explicitly adds the
|
|
// RegisterCandidate call in normal builds. In the future, however, we
|
|
// would probably like to have this detail expressed in C# rather than
|
|
// in the compiler sources.
|
|
[RequiredByBartok]
|
|
[NoInline]
|
|
[ManualRefCounts]
|
|
internal static Object AllocateFinalizableObject(VTable vtable) {
|
|
Object obj = AllocateObject(vtable);
|
|
Finalizer.RegisterCandidate(obj);
|
|
return obj;
|
|
}
|
|
|
|
[RequiredByBartok]
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
internal static Array AllocateVector(VTable vtable, int numElements)
|
|
{
|
|
return AllocateVector(vtable, numElements, Thread.CurrentThread);
|
|
}
|
|
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
internal static Array AllocateVector(VTable vtable,
|
|
int numElements,
|
|
Thread currentThread)
|
|
{
|
|
VTable.Deny(Transitions.UnderGCControl(currentThread.threadIndex));
|
|
return installedGC.AllocateVector(vtable, numElements,
|
|
currentThread);
|
|
}
|
|
|
|
[RequiredByBartok]
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
internal static Array AllocateArray(VTable vtable, int rank,
|
|
int totalElements)
|
|
{
|
|
return AllocateArray(vtable, rank, totalElements,
|
|
Thread.CurrentThread);
|
|
}
|
|
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
internal static Array AllocateArray(VTable vtable, int rank,
|
|
int totalElements,
|
|
Thread currentThread)
|
|
{
|
|
VTable.Deny(Transitions.UnderGCControl(currentThread.threadIndex));
|
|
return installedGC.AllocateArray(vtable, rank, totalElements,
|
|
currentThread);
|
|
}
|
|
|
|
[RequiredByBartok]
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
internal static String AllocateString(int stringLength)
|
|
{
|
|
return AllocateString(stringLength, Thread.CurrentThread);
|
|
}
|
|
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
internal static String AllocateString(int stringLength,
|
|
Thread currentThread)
|
|
{
|
|
VTable.Deny(Transitions.UnderGCControl(currentThread.threadIndex));
|
|
return installedGC.AllocateString(stringLength, currentThread);
|
|
}
|
|
|
|
public static int GetGeneration(Object obj)
|
|
{
|
|
return installedGC.GetGeneration(obj);
|
|
}
|
|
|
|
public static int MaxGeneration {
|
|
get { return installedGC.MaxGeneration; }
|
|
}
|
|
|
|
internal enum SpecialGeneration {
|
|
InvokeCollection = -1,
|
|
InvokeMajorCollection = -2,
|
|
// Not all collectors use these next two values.
|
|
InvokeStackScanOnly = -3,
|
|
InvokeStackFixupOnly = -4
|
|
}
|
|
|
|
internal static bool IsUserCollectionRequest(int gen)
|
|
{
|
|
return gen >= 0;
|
|
}
|
|
|
|
internal static bool IsInternalCollectionRequest(int gen)
|
|
{
|
|
return gen == (int)SpecialGeneration.InvokeCollection
|
|
|| gen == (int)SpecialGeneration.InvokeMajorCollection;
|
|
}
|
|
|
|
internal static bool IsRealCollectionRequest(int gen)
|
|
{
|
|
return gen >= 0
|
|
|| gen == (int)SpecialGeneration.InvokeCollection
|
|
|| gen == (int)SpecialGeneration.InvokeMajorCollection;
|
|
}
|
|
|
|
[NoInline]
|
|
[RequiredByBartok]
|
|
public static void KeepAlive(Object obj)
|
|
{
|
|
dummyGlobal = obj;
|
|
dummyGlobal = null;
|
|
}
|
|
|
|
public static void WaitForPendingFinalizers()
|
|
{
|
|
Finalizer.WaitForPending();
|
|
}
|
|
|
|
public static long GetTotalMemory(bool forceFullCollection)
|
|
{
|
|
long size = installedGC.TotalMemory;
|
|
if (!forceFullCollection) {
|
|
return size;
|
|
}
|
|
// If we force a full collection, we will run the finalizers on all
|
|
// existing objects and do a collection until the value stabilizes.
|
|
// The value is "stable" when either the value is within 5% of the
|
|
// previous call to installedGC.TotalMemory, or if we have been sitting
|
|
// here for more than x times (we don't want to loop forever here).
|
|
for (int reps = 0; reps < 8; reps++) {
|
|
WaitForPendingFinalizers();
|
|
Collect();
|
|
long newSize = installedGC.TotalMemory;
|
|
long bound = size / 20; // 5%
|
|
long diff = newSize - size;
|
|
size = newSize;
|
|
if (diff >= -bound && diff <= bound) {
|
|
break;
|
|
}
|
|
}
|
|
return size;
|
|
}
|
|
|
|
public static void SuppressFinalize(Object obj)
|
|
{
|
|
if (obj == null) {
|
|
throw new ArgumentNullException("obj");
|
|
}
|
|
Finalizer.SuppressCandidate(obj);
|
|
}
|
|
|
|
internal static void nativeSuppressFinalize(Object obj) {
|
|
Finalizer.SuppressCandidate(obj);
|
|
}
|
|
|
|
public static void ReRegisterForFinalize(Object obj)
|
|
{
|
|
if (obj == null) {
|
|
throw new ArgumentNullException("obj");
|
|
}
|
|
Finalizer.RegisterCandidate(obj);
|
|
}
|
|
|
|
public static int GetGeneration(WeakReference wo)
|
|
{
|
|
Object obj = wo.Target;
|
|
if (obj == null) {
|
|
throw new ArgumentException("wo", "target already collected");
|
|
}
|
|
return GetGeneration(obj);
|
|
}
|
|
|
|
public static void SetProfiler(GCProfiler profiler)
|
|
{
|
|
installedGC.SetProfiler(profiler);
|
|
isProfiling = true;
|
|
}
|
|
|
|
public static bool IsProfiling
|
|
{
|
|
get {
|
|
return isProfiling;
|
|
}
|
|
}
|
|
|
|
internal static void ProfileAllocation(Object obj)
|
|
{
|
|
if (isProfiling) {
|
|
installedGC.ProfileAllocation(obj);
|
|
}
|
|
}
|
|
|
|
private static void SetCleanupCache()
|
|
{
|
|
// REVIEW: will not ever clean up these caches (such as Assembly
|
|
// strong names)
|
|
}
|
|
|
|
internal static void EnableHeap()
|
|
{
|
|
VTable.Assert(GC.allocationGCInhibitCount == 1);
|
|
GC.allocationGCInhibitCount = 0;
|
|
CollectorStatistics.Initialize();
|
|
CollectorStatistics.Event(GCEvent.CreateHeap);
|
|
GC.installedGC.EnableHeap();
|
|
Finalizer.StartFinalizerThread();
|
|
}
|
|
|
|
internal static void Shutdown()
|
|
{
|
|
if(installedGC != null) {
|
|
installedGC.Shutdown();
|
|
}
|
|
}
|
|
|
|
// Called on VM shutdown.
|
|
internal static void DestructHeap()
|
|
{
|
|
if (installedGC != null) {
|
|
installedGC.DestructHeap();
|
|
}
|
|
}
|
|
|
|
internal static void NewThreadNotification(Thread newThread,
|
|
bool initial)
|
|
{
|
|
GC.installedGC.NewThreadNotification(newThread, initial);
|
|
}
|
|
|
|
internal static void DeadThreadNotification(Thread deadThread)
|
|
{
|
|
GC.installedGC.DeadThreadNotification(deadThread);
|
|
}
|
|
|
|
internal static void ThreadStartNotification(int currentThreadIndex)
|
|
{
|
|
GC.installedGC.ThreadStartNotification(currentThreadIndex);
|
|
}
|
|
|
|
internal static void ThreadEndNotification(Thread dyingThread)
|
|
{
|
|
GC.installedGC.ThreadEndNotification(dyingThread);
|
|
}
|
|
|
|
internal static void ThreadDormantGCNotification(int threadIndex)
|
|
{
|
|
GC.installedGC.ThreadDormantGCNotification(threadIndex);
|
|
}
|
|
|
|
}
|
|
|
|
}
|