356 lines
12 KiB
C#
356 lines
12 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. */
|
|
/*******************************************************************/
|
|
|
|
|
|
namespace System.GCs
|
|
{
|
|
|
|
using Microsoft.Bartok.Runtime;
|
|
|
|
using System.Threading;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
#if SINGULARITY
|
|
using Microsoft.Singularity;
|
|
#endif
|
|
|
|
#if SINGULARITY_KERNEL
|
|
using Microsoft.Singularity.Scheduling;
|
|
#endif
|
|
|
|
[NoCCtor]
|
|
internal abstract class BaseCollector : Collector
|
|
{
|
|
|
|
[Inline]
|
|
protected bool IsValidGeneration(int generation)
|
|
{
|
|
return ((generation >= MinGeneration) &&
|
|
(generation <= MaxGeneration));
|
|
}
|
|
|
|
#region Heap Lifetime Events
|
|
|
|
internal override void Shutdown()
|
|
{
|
|
}
|
|
|
|
internal override void DestructHeap()
|
|
{
|
|
PrintGCTiming();
|
|
PrintAllocations();
|
|
CollectorStatistics.Event(GCEvent.DestroyHeap);
|
|
CollectorStatistics.Summary();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Notifications
|
|
|
|
internal override void NewThreadNotification(Thread newThread,
|
|
bool initial)
|
|
{
|
|
Transitions.NewThreadNotification(newThread.threadIndex, initial);
|
|
}
|
|
|
|
internal override void DeadThreadNotification(Thread deadThread)
|
|
{
|
|
}
|
|
|
|
internal override void ThreadStartNotification(int currentThreadIndex)
|
|
{
|
|
#if !SINGULARITY
|
|
Thread currentThread = Thread.threadTable[currentThreadIndex];
|
|
PageManager.MarkThreadStack(currentThread);
|
|
#endif
|
|
}
|
|
|
|
internal override void ThreadEndNotification(Thread currentThread)
|
|
{
|
|
}
|
|
|
|
internal override void ThreadDormantGCNotification(int threadIndex)
|
|
{
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Allocation
|
|
|
|
[ManualRefCounts]
|
|
[Inline]
|
|
internal override Object AllocateObject(VTable vtable,
|
|
Thread currentThread)
|
|
{
|
|
return AllocateObject(vtable,
|
|
ObjectLayout.ObjectSize(vtable),
|
|
vtable.baseAlignment,
|
|
currentThread);
|
|
}
|
|
|
|
[ManualRefCounts]
|
|
[Inline]
|
|
internal override Object AllocateObject(VTable vtable,
|
|
UIntPtr numBytes,
|
|
uint baseAlignment,
|
|
Thread currentThread)
|
|
{
|
|
UIntPtr objectAddr =
|
|
AllocateObjectMemory(numBytes, baseAlignment,
|
|
currentThread);
|
|
Object result = Magic.fromAddress(objectAddr);
|
|
this.CreateObject(result, vtable, currentThread);
|
|
return result;
|
|
}
|
|
|
|
[ManualRefCounts]
|
|
internal override Array AllocateVector(VTable vtable,
|
|
int numElements,
|
|
Thread currentThread)
|
|
{
|
|
UIntPtr numBytes =
|
|
ObjectLayout.ArraySize(vtable, unchecked((uint)numElements));
|
|
UIntPtr vectorAddr =
|
|
AllocateObjectMemory(numBytes, vtable.baseAlignment,
|
|
currentThread);
|
|
Array result = Magic.toArray(Magic.fromAddress(vectorAddr));
|
|
CreateObject(result, vtable, currentThread);
|
|
result.InitializeVectorLength(numElements);
|
|
return result;
|
|
}
|
|
|
|
[ManualRefCounts]
|
|
internal override Array AllocateArray(VTable vtable,
|
|
int rank,
|
|
int totalElements,
|
|
Thread currentThread)
|
|
{
|
|
UIntPtr numBytes =
|
|
ObjectLayout.ArraySize(vtable, unchecked((uint)totalElements));
|
|
UIntPtr arrayAddr =
|
|
AllocateObjectMemory(numBytes, vtable.baseAlignment,
|
|
currentThread);
|
|
Array result = Magic.toArray(Magic.fromAddress(arrayAddr));
|
|
CreateObject(result, vtable, currentThread);
|
|
result.InitializeArrayLength(rank, totalElements);
|
|
return result;
|
|
}
|
|
|
|
[ManualRefCounts]
|
|
internal override String AllocateString(int stringLength,
|
|
Thread currentThread)
|
|
{
|
|
VTable vtable =
|
|
Magic.toRuntimeType(typeof(System.String)).classVtable;
|
|
UIntPtr numBytes =
|
|
ObjectLayout.StringSize(vtable,
|
|
unchecked((uint) (stringLength+1)));
|
|
UIntPtr stringAddr =
|
|
AllocateObjectMemory(numBytes, unchecked((uint) UIntPtr.Size),
|
|
currentThread);
|
|
String result = Magic.toString(Magic.fromAddress(stringAddr));
|
|
CreateObject(result, vtable, currentThread);
|
|
result.InitializeStringLength(stringLength);
|
|
return result;
|
|
}
|
|
|
|
[ManualRefCounts]
|
|
[AssertDevirtualize]
|
|
[Inline]
|
|
protected virtual void CreateObject(Object obj, VTable vtable,
|
|
Thread currentThread)
|
|
{
|
|
Barrier.InitObject(obj, vtable);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Profiling
|
|
|
|
internal override void SetProfiler(GCProfiler profiler) {
|
|
if (GcProfiler != null) {
|
|
throw new InvalidOperationException("Only one GCProfiler can be active in a process");
|
|
}
|
|
ProfileRoots = new ProfileRootsDelegate(ProfileScanRoots);
|
|
ProfileObjects = new ProfileObjectsDelegate(ProfileScanObjects);
|
|
|
|
GcProfiler = profiler;
|
|
}
|
|
|
|
// A profiler can request a scan of all Roots, passing in a
|
|
// visitor for callback.
|
|
private void ProfileScanRoots(NonNullReferenceVisitor visitor) {
|
|
CallStack.ScanStacks(visitor, visitor);
|
|
Thread.VisitBootstrapData(visitor);
|
|
#if SINGULARITY_KERNEL
|
|
Kernel.VisitSpecialData(visitor);
|
|
#endif
|
|
MultiUseWord.VisitStrongRefs(visitor,
|
|
false /* Don't use shadows */);
|
|
StaticData.ScanStaticData(visitor);
|
|
}
|
|
|
|
// A profiler can request a scan of all Objects in the heap,
|
|
// passing in a visitor for callback.
|
|
private
|
|
void ProfileScanObjects(SegregatedFreeList.ObjectVisitor visitor)
|
|
{
|
|
#if !SINGULARITY
|
|
VTable.Assert((System.GC.installedGC as MarkSweepCollector != null)
|
|
|| (System.GC.installedGC as ConcurrentMSCollector != null)
|
|
|| (System.GC.installedGC as ReferenceCountingCollector != null)
|
|
|| (System.GC.installedGC as DeferredReferenceCountingCollector != null),
|
|
"ProfileScanObjects is only valid for MarkSweep, ConcurrentMS, "
|
|
+ "ReferenceCounting, and DeferredReferenceCounting collectors");
|
|
#endif
|
|
SegregatedFreeList.VisitAllObjects(visitor);
|
|
}
|
|
|
|
internal override void ProfileAllocation(Object obj)
|
|
{
|
|
if (GC.IsProfiling && !HeapDamaged) {
|
|
UIntPtr size = ObjectLayout.Sizeof(obj);
|
|
GcProfiler.NotifyAllocation(Magic.addressOf(obj),
|
|
obj.GetType(), size);
|
|
}
|
|
}
|
|
|
|
protected static GCProfiler GcProfiler;
|
|
protected static ProfileRootsDelegate ProfileRoots;
|
|
protected static ProfileObjectsDelegate ProfileObjects;
|
|
protected static bool HeapDamaged;
|
|
|
|
#endregion
|
|
|
|
#region Accounting
|
|
|
|
private static UIntPtr newBytesSinceGC;
|
|
internal static long gcTotalBytes;
|
|
internal static int gcTotalCount;
|
|
internal static long gcTotalTime;
|
|
private static long maxPauseTime;
|
|
private static long pauseCount;
|
|
|
|
// Abstraction violation. Use only for debugging!
|
|
internal static UIntPtr DebugNewBytesSinceGC {
|
|
get { return newBytesSinceGC; }
|
|
}
|
|
|
|
internal static bool NewBytesSinceGCExceeds(UIntPtr limit)
|
|
{
|
|
return newBytesSinceGC >= limit;
|
|
}
|
|
|
|
internal static void IncrementNewBytesSinceGC(UIntPtr increment)
|
|
{
|
|
newBytesSinceGC += increment;
|
|
}
|
|
|
|
internal static void StartGCCycle()
|
|
{
|
|
gcTotalBytes += (long) newBytesSinceGC;
|
|
newBytesSinceGC = UIntPtr.Zero;
|
|
gcTotalCount++;
|
|
}
|
|
|
|
internal static void RegisterPause(int pauseTicks)
|
|
{
|
|
gcTotalTime += pauseTicks;
|
|
if (maxPauseTime < pauseTicks) {
|
|
maxPauseTime = pauseTicks;
|
|
}
|
|
pauseCount++;
|
|
}
|
|
|
|
internal virtual void RegisterHeapSize(ulong heapSize)
|
|
{
|
|
}
|
|
|
|
internal virtual void RegisterNewObject(ulong objectSize)
|
|
{
|
|
}
|
|
|
|
internal virtual void PrintGCTiming()
|
|
{
|
|
if (VTable.enableGCTiming || VTable.enableFinalGCTiming) {
|
|
#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 (BaseCollector.pauseCount != 0) {
|
|
Console.Error.WriteLine("Avg. Pause Time (ms): "+
|
|
gcTotalTime/pauseCount);
|
|
} else {
|
|
Console.Error.WriteLine("Avg. Pause Time (ms): 0");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
internal virtual void PrintAllocations()
|
|
{
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Helper functions
|
|
|
|
internal static void AllThreadRendezvous(int currentThreadIndex)
|
|
{
|
|
Transitions.MakeGCRequests(currentThreadIndex);
|
|
for (int i = 0; i < Thread.threadTable.Length; i++) {
|
|
if (Thread.threadTable[i] == null ||
|
|
i == currentThreadIndex) {
|
|
continue;
|
|
}
|
|
CollectorStatistics.Event(GCEvent.StopThread, i);
|
|
while (!Transitions.TakeGCControl(i) &&
|
|
!Transitions.UnderGCControl(i) &&
|
|
Transitions.HasGCRequest(i) &&
|
|
Thread.threadTable[i] != null) {
|
|
// NOTE: there is no code in this loop that could
|
|
// cause a signal on an event to be consumed.
|
|
Thread.WaitForGCEvent(currentThreadIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static void AllThreadRelease(int currentThreadIndex)
|
|
{
|
|
for (int i = 0; i < Thread.threadTable.Length; i++) {
|
|
#if SINGULARITY_KERNEL
|
|
if (Scheduler.IsIdleThread(i)) {
|
|
continue;
|
|
}
|
|
#endif
|
|
if (i == currentThreadIndex) {
|
|
if (Transitions.HasGCRequest(i)) {
|
|
Transitions.ClearGCRequest(i);
|
|
}
|
|
} else if (Transitions.UnderGCControl(i)) {
|
|
Transitions.ReleaseGCControl(i);
|
|
}
|
|
// Signal all threads to ensure that the GC process didn't
|
|
// accidentally "gobble" an event signal that was meant for
|
|
// something else.
|
|
if (Thread.threadTable[i] != null) {
|
|
Thread.SignalGCEvent(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
}
|