// // 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); } } }