/*******************************************************************/ /* 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. */ /*******************************************************************/ // // Copyright (c) Microsoft Corporation. All rights reserved. // 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; using Microsoft.Singularity.X86; #endif // The GC has only static members and doesn't require the serializable // keyword. [CCtorIsRunDuringStartup] [RequiredByBartok] [CLSCompliant(false)] public sealed class GC { // Bartok runtime "magic" function // It saves the callee-save registers in a transition record and // calls System.GC.CollectBody(thread, generation) [MethodImpl(MethodImplOptions.InternalCall)] [GCAnnotation(GCOption.GCFRIEND)] [StackBound(128)] private static extern void CollectBodyTransition(Thread thread, int generation); internal static int gcTotalCount; internal static long gcTotalTime; internal static long maxPauseTime; internal static long pauseCount; internal static long gcTotalBytes; internal static ulong bytesAllocated; internal static ulong objectsAllocated; #if SINGULARITY_KERNEL internal static uint perfCounter = 6; #else internal static uint perfCounter = 5; #endif [RequiredByBartok] [TrustedNonNull] internal static Collector installedGC; private static bool isProfiling; private static Object dummyGlobal; // Used by KeepAlive [AccessedByRuntime("referenced in halasm.asm/brtasm.asm")] internal static bool allocationInhibitGC = false; internal static UIntPtr newBytesSinceGC; [Intrinsic] internal static GCType gcType; [Intrinsic] internal static WBType wbType; [Intrinsic] internal static RemSetType remsetType; [Intrinsic] internal static CopyScanType copyscanType; #if !SINGULARITY [NoStackLinkCheck] #endif [PreInitRefCounts] internal static void ConstructHeap() { PageTable.Initialize(); MemoryManager.Initialize(); #if 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 = UIntPtr.Size == 8 ? (UIntPtr) 1 << 15 : (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); } // 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) { 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 case GCType.NullCollector: { VTable.Assert(wbType == 0, "No need for a write barrier"); GC.Initialize(); GC.installedGC = (NullCollector) BootstrapMemory.Allocate(typeof(NullCollector)); break; } #endif default: { VTable.NotReached("Unknown GC type: "+gcType); break; } } GC.installedGC.NewThreadNotification(Thread.initialThread, true); GC.installedGC.ThreadStartNotification(Thread.initialThread.threadIndex); } [PreInitRefCounts] internal static void Initialize() { Transitions.Initialize(); WriteBarrier.Initialize(); } private static void FinishedGCCycle() { gcTotalCount++; gcTotalBytes += (long) newBytesSinceGC; newBytesSinceGC = UIntPtr.Zero; } #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 = gcTotalCount; collectorMillis = gcTotalTime; collectorBytes = gcTotalBytes; } #endif // Garbage Collect all generations. public static void Collect() { CollectBodyTransition(Thread.CurrentThread, MaxGeneration); } public static void Collect(int generation) { if (generation < 0) { throw new ArgumentOutOfRangeException( "generation", "Argument should be positive!"); } CollectBodyTransition(Thread.CurrentThread, generation); } internal static void InvokeCollection(Thread currentThread) { CollectBodyTransition(currentThread, -1); } internal static void InvokeMajorCollection(Thread currentThread) { CollectBodyTransition(currentThread, -2); } // 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] [AccessedByRuntime("called from halforgc.asm/brtforgc.asm")] private static unsafe Thread CollectBody(Thread currentThread, int generation) { int startTicks = 0; bool enableGCTiming = VTable.enableGCTiming; if (enableGCTiming) { VTable.enableGCTiming = false; pauseCount++; startTicks = Environment.TickCount; VTable.DebugPrint("[GC start: {0} bytes]\n", __arglist(installedGC.TotalMemory)); } if (VTable.enableGCWatermarks) { MemoryAccounting.RecordHeapWatermarks(); } int currentThreadIndex = currentThread.threadIndex; // Our stack is GC safe after going through CollectBodyTransition installedGC.Collect(currentThreadIndex, generation); FinishedGCCycle(); if (VTable.enableGCWatermarks) { MemoryAccounting.RecordHeapWatermarks(); } if (enableGCTiming) { int elapsedTicks = Environment.TickCount - startTicks; gcTotalTime += elapsedTicks; if (maxPauseTime < elapsedTicks) { maxPauseTime = elapsedTicks; } VTable.DebugPrint("[GC end : {0} bytes, {1} ms]\n", __arglist(installedGC.TotalMemory, elapsedTicks)); VTable.enableGCTiming = true; } return Thread.threadTable[currentThreadIndex]; } [RequiredByBartok] [AccessedByRuntime("called from brtasm.asm")] [Inline] [ManualRefCounts] internal static Object AllocateObject(VTable vtable) { return AllocateObject(vtable, Thread.CurrentThread); } [RequiredByBartok] [Inline] [ManualRefCounts] internal static Object AllocateObject(VTable vtable, Thread currentThread) { VTable.Deny(Transitions.UnderGCControl(currentThread.threadIndex)); return installedGC.AllocateObject(vtable, currentThread); } [NoInline] [ManualRefCounts] internal static Object AllocateObjectNoInline(VTable vtable, Thread currentThread) { return GC.AllocateObject(vtable, currentThread); } [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; } } [NoInline] 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() { CollectorStatistics.Initialize(); CollectorStatistics.Event(GCEvent.CreateHeap); GC.installedGC.EnableHeap(); Finalizer.StartFinalizerThread(); } // Called on VM shutdown. internal static void DestructHeap() { if (VTable.enableGCWatermarks) { MemoryAccounting.RecordHeapWatermarks(); MemoryAccounting.ReportHeapWatermarks(); } if (VTable.enableGCTiming) { #if SINGULARITY DebugStub.WriteLine("Total GC Time (ms): {0}", __arglist(gcTotalTime)); #else Console.Error.WriteLine("Total GC Time (ms): " + gcTotalTime); Console.Error.WriteLine("Max. Pause Time (ms): " + maxPauseTime); if (pauseCount != 0) { Console.Error.WriteLine("Avg. Pause Time (ms): " + gcTotalTime/pauseCount); } else { Console.Error.WriteLine("Avg. Pause Time (ms): 0"); } #endif } if (VTable.enableGCProfiling) { #if !SINGULARITY Console.Error.WriteLine("Objects allocated: "+ objectsAllocated); Console.Error.WriteLine("Total bytes allocated (KB): "+ (bytesAllocated >> 10)); #endif } if(installedGC != null) { installedGC.DestructHeap(); } CollectorStatistics.Event(GCEvent.DestroyHeap); CollectorStatistics.Summary(); } 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); } } }