540 lines
22 KiB
C#
540 lines
22 KiB
C#
/*******************************************************************/
|
|
/* 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.GCs {
|
|
|
|
using System.Threading;
|
|
using System.Runtime.CompilerServices;
|
|
using Microsoft.Bartok.Runtime;
|
|
|
|
internal class MemoryAccounting {
|
|
private static BumpAllocator localArea;
|
|
|
|
private static ManagedPtrAccounting traceablePtrs;
|
|
private static ManagedPtrAccounting traceablePinnedPtrs;
|
|
private static StaticPtrAccounting staticPtrs;
|
|
|
|
private static AssertRTypeHeaders assertRTypeHeaders;
|
|
private static ObjectLayout.ObjectVisitor forGCAssertRTypeHeaders;
|
|
private static RuntimeTypeReckoner runtimeTypeReckoner;
|
|
private static ObjectLayout.ObjectVisitor forGCRuntimeTypeReckoner;
|
|
private static RuntimeTypeMapper runtimeTypeMapper;
|
|
private static ObjectLayout.ObjectVisitor forGCRuntimeTypeMapper;
|
|
private static InstanceReckoner instanceReckoner;
|
|
private static ObjectLayout.ObjectVisitor forGCInstanceReckoner;
|
|
|
|
private const int TABLE_SIZE = 1024;
|
|
private static RuntimeTypeAccounting[] table;
|
|
|
|
private static UIntPtr totalSize;
|
|
|
|
private static bool initialized;
|
|
|
|
internal static long MaxHeapWatermark;
|
|
internal static double AvgHeapSize;
|
|
internal static long Watermarks;
|
|
|
|
internal unsafe static void Initialize(GCType gcType) {
|
|
traceablePtrs = new ManagedPtrAccounting();
|
|
traceablePinnedPtrs = new ManagedPtrAccounting();
|
|
staticPtrs = new StaticPtrAccounting();
|
|
|
|
assertRTypeHeaders = new AssertRTypeHeaders();
|
|
forGCAssertRTypeHeaders = assertRTypeHeaders;
|
|
runtimeTypeReckoner = new RuntimeTypeReckoner();
|
|
forGCRuntimeTypeReckoner = runtimeTypeReckoner;
|
|
runtimeTypeMapper = new RuntimeTypeMapper();
|
|
forGCRuntimeTypeMapper = runtimeTypeMapper;
|
|
instanceReckoner = new InstanceReckoner();
|
|
forGCInstanceReckoner = instanceReckoner;
|
|
|
|
switch(gcType) {
|
|
case GCType.MarkSweepCollector: {
|
|
forGCAssertRTypeHeaders = new SegregatedFreeList.ObjectVisitorWrapper(assertRTypeHeaders);
|
|
forGCRuntimeTypeReckoner = new SegregatedFreeList.ObjectVisitorWrapper(runtimeTypeReckoner);
|
|
forGCRuntimeTypeMapper = new SegregatedFreeList.ObjectVisitorWrapper(runtimeTypeMapper);
|
|
forGCInstanceReckoner = new SegregatedFreeList.ObjectVisitorWrapper(instanceReckoner);
|
|
break;
|
|
}
|
|
}
|
|
|
|
table = new RuntimeTypeAccounting[TABLE_SIZE];
|
|
|
|
MemoryAccounting.initialized = true;
|
|
}
|
|
|
|
internal unsafe static void Report(GCType gcType) {
|
|
if(!MemoryAccounting.initialized) {
|
|
VTable.DebugPrint("MemoryAccounting invoked before"
|
|
+ " initialization was completed!");
|
|
return;
|
|
}
|
|
|
|
VTable.DebugPrint("\nCollector: ");
|
|
switch(gcType) {
|
|
case GCType.AdaptiveCopyingCollector: {
|
|
VTable.DebugPrint("Adaptive Copying\n");
|
|
break;
|
|
}
|
|
case GCType.MarkSweepCollector: {
|
|
VTable.DebugPrint("Mark-Sweep\n");
|
|
break;
|
|
}
|
|
case GCType.SemispaceCollector: {
|
|
VTable.DebugPrint("Semispace\n");
|
|
break;
|
|
}
|
|
case GCType.SlidingCollector: {
|
|
VTable.DebugPrint("Sliding\n");
|
|
break;
|
|
}
|
|
case GCType.ConcurrentMSCollector: {
|
|
VTable.DebugPrint("Concurrent Mark-Sweep\n");
|
|
break;
|
|
}
|
|
case GCType.ReferenceCountingCollector: {
|
|
VTable.DebugPrint("Reference Counting\n");
|
|
VTable.NotImplemented();
|
|
return; // Not supported yet.
|
|
}
|
|
#if !SINGULARITY
|
|
case GCType.DeferredReferenceCountingCollector: {
|
|
VTable.DebugPrint("Deferred Reference Counting\n");
|
|
VTable.NotImplemented();
|
|
return; // Not supported yet.
|
|
}
|
|
#endif
|
|
default: {
|
|
VTable.NotReached("Unknown GC type: "+gcType);
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint pageSize = (uint)(PageTable.PageSize >> 10);
|
|
VTable.DebugPrint("\nPage size: {0}KB\nVM size: {1} KB\n",
|
|
__arglist(pageSize,
|
|
pageSize*(uint)PageTable.pageTableCount));
|
|
|
|
ReportNonGCDetails();
|
|
ReportStackDetails();
|
|
ReportHeapDetails();
|
|
}
|
|
|
|
internal unsafe static void ReportNonGCDetails() {
|
|
VTable.DebugPrint("\nNon-GC data details:\n");
|
|
uint totalPageCount = TotalNumPages(PageType.NonGC);
|
|
VTable.DebugPrint("\tTotal number of pages: {0}",
|
|
__arglist(totalPageCount));
|
|
|
|
VTable.DebugPrint("\n\tMemory accounting storage: {0}B",
|
|
__arglist((uint)totalSize));
|
|
|
|
staticPtrs.Initialize();
|
|
uint staticSize = StaticData.ScanStaticPointerData(staticPtrs);
|
|
VTable.DebugPrint("\n\tCompile-time allocated data: {0}B",
|
|
__arglist(staticSize));
|
|
VTable.DebugPrint("\n\tNumber of pointers: {0}",
|
|
__arglist(staticPtrs.PtrCount));
|
|
uint bootstrapPageCount = totalPageCount -
|
|
(uint)PageTable.PageCount((UIntPtr)staticSize);
|
|
VTable.DebugPrint("\n\tNumber of bootstrap pages: {0}\n",
|
|
__arglist(bootstrapPageCount));
|
|
}
|
|
|
|
internal unsafe static void ReportStackDetails() {
|
|
VTable.DebugPrint("\nStack details:\n");
|
|
VTable.DebugPrint("\tTotal number of reserved pages: {0}",
|
|
__arglist(TotalNumPages(PageType.Stack)));
|
|
|
|
for (int i = 0; i < Thread.threadTable.Length; i++) {
|
|
traceablePtrs.Initialize();
|
|
traceablePinnedPtrs.Initialize();
|
|
|
|
Thread t = Thread.threadTable[i];
|
|
if (t == null) {
|
|
continue;
|
|
}
|
|
|
|
uint numStackFrames =
|
|
CallStack.ScanStack(t, traceablePtrs,
|
|
traceablePinnedPtrs);
|
|
|
|
VTable.DebugPrint("\n\tThread: {0}",
|
|
__arglist(i));
|
|
VTable.DebugPrint("\n\t\tStack frame count: {0}",
|
|
__arglist(numStackFrames));
|
|
VTable.DebugPrint("\n\t\tManaged ptrs to static data: {0}",
|
|
__arglist
|
|
(traceablePtrs.managedPtrsToStaticData));
|
|
VTable.DebugPrint("\n\t\tManaged ptrs to stack: {0}",
|
|
__arglist(traceablePtrs.managedPtrsToStack));
|
|
VTable.DebugPrint("\n\t\tExterior managed heap ptrs: {0}",
|
|
__arglist
|
|
(traceablePtrs.exteriorManagedHeapPtrs));
|
|
VTable.DebugPrint("\n\t\tInterior managed heap ptrs: {0}",
|
|
__arglist
|
|
(traceablePtrs.interiorManagedHeapPtrs));
|
|
|
|
#if !SINGULARITY
|
|
UIntPtr stackTop = (UIntPtr) CallStack.StackMarker(t);
|
|
uint stackSize = 0;
|
|
if (stackTop != UIntPtr.Zero) {
|
|
VTable.Assert(CallStack.StackBase(t) > stackTop,
|
|
@"t.asmStackBase > stackTop");
|
|
|
|
stackSize = (uint)(CallStack.StackBase(t)-stackTop);
|
|
}
|
|
VTable.DebugPrint("\n\t\tApprox. stack size: {0}B",
|
|
__arglist(stackSize));
|
|
#endif
|
|
|
|
// REVIEW: Why are these here?
|
|
VTable.Assert(traceablePinnedPtrs.
|
|
managedPtrsToStaticData == 0,
|
|
@"traceablePinnedPtrs.
|
|
managedPtrsToStaticData == 0");
|
|
VTable.Assert(traceablePinnedPtrs.
|
|
managedPtrsToStack == 0,
|
|
@"traceablePinnedPtrs.
|
|
managedPtrsToStack == 0");
|
|
|
|
VTable.DebugPrint("\n\t\tPinned managed heap ptrs: {0}",
|
|
__arglist
|
|
(traceablePinnedPtrs.exteriorManagedHeapPtrs +
|
|
traceablePinnedPtrs.interiorManagedHeapPtrs));
|
|
}
|
|
|
|
VTable.DebugPrint("\n");
|
|
}
|
|
|
|
internal static void ReportHeapDetails() {
|
|
VTable.DebugPrint("\nHeap details:\n");
|
|
uint pageCount = 0;
|
|
for (UIntPtr i = UIntPtr.Zero; i < PageTable.pageTableCount;
|
|
i++) {
|
|
if (PageTable.IsMyGcPage(i)) {
|
|
pageCount++;
|
|
}
|
|
}
|
|
VTable.DebugPrint("\tTotal number of heap pages: {0}",
|
|
__arglist(pageCount));
|
|
|
|
// The following obtains counts of heap objects against types.
|
|
UIntPtr lowPage = UIntPtr.Zero;
|
|
UIntPtr highPage = PageTable.pageTableCount;
|
|
assertRuntimeTypeHeaders(lowPage, highPage);
|
|
|
|
// First count the RuntimeType instances for heap objects.
|
|
runtimeTypeReckoner.Initialize(true);
|
|
visitAllObjects(forGCRuntimeTypeReckoner, lowPage, highPage);
|
|
|
|
// Next, create a table for RuntimeType instance accounting.
|
|
// NOTE: Storage for the table is marked "non-GC". Since
|
|
// static data accounting is done before this, it's okay.
|
|
int numSlots = runtimeTypeReckoner.Count;
|
|
if(numSlots > TABLE_SIZE) {
|
|
VTable.DebugPrint("Need {0} slots, have {1}\n",
|
|
__arglist(numSlots, TABLE_SIZE));
|
|
VTable.NotReached("MemoryAccounting table not large enough");
|
|
}
|
|
|
|
// Associate a table slot for each RuntimeType instance.
|
|
runtimeTypeMapper.Initialize(false, MemoryAccounting.table);
|
|
visitAllObjects(forGCRuntimeTypeMapper, lowPage, highPage);
|
|
|
|
// Map each relevant RuntimeType instance to its table slot.
|
|
for (uint i = 0; i < numSlots; i++) {
|
|
RuntimeType rType = MemoryAccounting.table[i].RuntimeTypeObject;
|
|
VTable.Assert(!MultiUseWord.IsMarked(rType),
|
|
@"!MultiUseWord.IsMarked(rType)");
|
|
MemoryAccounting.table[i].SavedMUW =
|
|
MultiUseWord.GetForObject(rType);
|
|
MultiUseWord.SetValForObject(rType, (UIntPtr)i);
|
|
}
|
|
|
|
// Count heap object instances by RuntimeType using table.
|
|
instanceReckoner.Initialize(MemoryAccounting.table);
|
|
visitAllObjects(forGCInstanceReckoner, lowPage, highPage);
|
|
|
|
// Bubble sort the table in decreasing order of total size.
|
|
for (int i = 0; i < numSlots; i++) {
|
|
for (int j = numSlots-1; j > i; j--) {
|
|
if (MemoryAccounting.table[j].TotalSize
|
|
> MemoryAccounting.table[j-1].TotalSize) {
|
|
// Swap contents.
|
|
RuntimeTypeAccounting temp = MemoryAccounting.table[j];
|
|
MemoryAccounting.table[j] = MemoryAccounting.table[j-1];
|
|
MemoryAccounting.table[j-1] = temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Display table.
|
|
VTable.DebugPrint("\n\tCounts of objects against types:\n");
|
|
for (uint i = 0; i < numSlots; i++) {
|
|
if((uint)MemoryAccounting.table[i].TotalSize < 1024) {
|
|
continue;
|
|
}
|
|
VTable.DebugPrint
|
|
("\t\t{0,36} instances: {1,6}, bytes: {2,10}\n",
|
|
__arglist(MemoryAccounting.table[i].RuntimeTypeObject.Name,
|
|
(uint)MemoryAccounting.table[i].Count,
|
|
(uint)MemoryAccounting.table[i].TotalSize));
|
|
}
|
|
|
|
// Reset book-keeping information maintained in headers and the global
|
|
// table.
|
|
for (uint i = 0; i < numSlots; i++) {
|
|
RuntimeType rType = MemoryAccounting.table[i].RuntimeTypeObject;
|
|
MultiUseWord.SetForObject
|
|
(rType, MemoryAccounting.table[i].SavedMUW);
|
|
VTable.Assert(!MultiUseWord.IsMarked(rType),
|
|
"@!MultiUseWord.IsMarked(rType)");
|
|
|
|
MemoryAccounting.table[i].RuntimeTypeObject = null;
|
|
MemoryAccounting.table[i].SavedMUW =
|
|
new MultiUseWord(new UIntPtr(0));
|
|
MemoryAccounting.table[i].TotalSize = new UIntPtr(0);
|
|
MemoryAccounting.table[i].Count = 0;
|
|
}
|
|
}
|
|
|
|
internal static void ReportHeapWatermarks() {
|
|
VTable.DebugPrint("Max. Heap: {0}KB ",
|
|
__arglist(MaxHeapWatermark >> 10));
|
|
VTable.DebugPrint("Avg. Heap: {0}KB ",
|
|
__arglist(((ulong)AvgHeapSize) >> 10));
|
|
VTable.DebugPrint("\n");
|
|
}
|
|
|
|
[ManualRefCounts]
|
|
internal static void RecordHeapWatermarks() {
|
|
long totalMemory = GC.installedGC.TotalMemory;
|
|
if (totalMemory > MaxHeapWatermark) {
|
|
MaxHeapWatermark = totalMemory;
|
|
}
|
|
AvgHeapSize =
|
|
(AvgHeapSize*Watermarks+totalMemory)/(Watermarks+1);
|
|
Watermarks++;
|
|
}
|
|
|
|
internal static uint TotalNumPages(PageType kind) {
|
|
uint pageCount = 0;
|
|
for (UIntPtr i = UIntPtr.Zero; i < PageTable.pageTableCount;
|
|
i++) {
|
|
if (PageTable.IsMyPage(i)) {
|
|
PageType pageType = PageTable.Type(i);
|
|
if (pageType == kind) {
|
|
pageCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pageCount;
|
|
}
|
|
|
|
private static void visitAllObjects(ObjectLayout.ObjectVisitor visitor,
|
|
UIntPtr lowPage, UIntPtr highPage)
|
|
{
|
|
for (UIntPtr first = lowPage; first < highPage; first++) {
|
|
if (PageTable.IsMyGcPage(first)) {
|
|
UIntPtr last = first+1;
|
|
while (last < highPage) {
|
|
if (!PageTable.IsMyGcPage(last)) {
|
|
break;
|
|
}
|
|
last++;
|
|
}
|
|
UIntPtr start = PageTable.PageAddr(first);
|
|
UIntPtr end = PageTable.PageAddr(last);
|
|
GC.installedGC.VisitObjects(visitor, start, end);
|
|
first = last;
|
|
}
|
|
}
|
|
}
|
|
|
|
[System.Diagnostics.Conditional("DEBUG")]
|
|
private static void assertRuntimeTypeHeaders(UIntPtr lowPage,
|
|
UIntPtr highPage) {
|
|
visitAllObjects(forGCAssertRTypeHeaders, lowPage, highPage);
|
|
}
|
|
|
|
|
|
private class ManagedPtrAccounting : NonNullReferenceVisitor {
|
|
internal uint managedPtrsToStaticData;
|
|
internal uint managedPtrsToStack;
|
|
internal uint interiorManagedHeapPtrs;
|
|
internal uint exteriorManagedHeapPtrs;
|
|
|
|
internal void Initialize() {
|
|
managedPtrsToStaticData = 0;
|
|
managedPtrsToStack = 0;
|
|
interiorManagedHeapPtrs = 0;
|
|
exteriorManagedHeapPtrs = 0;
|
|
}
|
|
|
|
internal override unsafe void Visit(UIntPtr* loc) {
|
|
// <loc> is a traceable pointer; its referent
|
|
// either resides in the heap, the stack or in
|
|
// the static data area.
|
|
UIntPtr addr = *loc;
|
|
|
|
UIntPtr page = PageTable.Page(addr);
|
|
VTable.Assert(PageTable.IsMyPage(page),
|
|
"MemoryAccounting: !IsMyPage");
|
|
if (!PageTable.IsGcPage(page)) {
|
|
PageType pageType = PageTable.Type(page);
|
|
VTable.Assert(pageType == PageType.NonGC ||
|
|
pageType == PageType.Stack ||
|
|
pageType == PageType.Shared,
|
|
"unexpected page type");
|
|
|
|
if (pageType == PageType.NonGC) {
|
|
// A managed pointer into the static data area.
|
|
managedPtrsToStaticData++;
|
|
} else {
|
|
// A managed pointer into the stack area.
|
|
managedPtrsToStack++;
|
|
}
|
|
return;
|
|
}
|
|
|
|
UIntPtr objAddr = GC.installedGC.FindObjectAddr(addr);
|
|
if (objAddr != addr) {
|
|
// A "truly" interior pointer into a heap object.
|
|
interiorManagedHeapPtrs++;
|
|
} else {
|
|
exteriorManagedHeapPtrs++;
|
|
}
|
|
}
|
|
}
|
|
|
|
private class StaticPtrAccounting : NonNullReferenceVisitor {
|
|
internal uint PtrCount;
|
|
|
|
internal void Initialize() {
|
|
PtrCount = 0;
|
|
}
|
|
|
|
internal override unsafe void Visit(UIntPtr* loc) {
|
|
// <loc> is an address of a reference location
|
|
// in the static data area.
|
|
UIntPtr page = PageTable.Page((UIntPtr)loc);
|
|
PageType pageType = PageTable.Type(page);
|
|
VTable.Assert(pageType == PageType.NonGC,
|
|
@"pageType == PageType.NonGC");
|
|
|
|
PtrCount++;
|
|
}
|
|
}
|
|
|
|
private struct RuntimeTypeAccounting {
|
|
// Reference to the accounted RuntimeType object.
|
|
internal RuntimeType RuntimeTypeObject;
|
|
|
|
// Copy of the accounted RuntimeType MultiUseWord.
|
|
internal MultiUseWord SavedMUW;
|
|
|
|
// Total size of all instances having this RuntimeType.
|
|
internal UIntPtr TotalSize;
|
|
|
|
// Number of instances having this RuntimeType.
|
|
internal uint Count;
|
|
}
|
|
|
|
private class AssertRTypeHeaders : ObjectLayout.ObjectVisitor {
|
|
internal override unsafe UIntPtr Visit(Object obj) {
|
|
VTable vtable = obj.vtable;
|
|
RuntimeType rType = vtable.vtableType;
|
|
VTable.Assert(!MultiUseWord.IsMarked(rType),
|
|
"@!MultiUseWord.IsMarked(rType)");
|
|
return ObjectLayout.ObjectSize(Magic.addressOf(obj), vtable);
|
|
}
|
|
}
|
|
|
|
private class RuntimeTypeReckoner : ObjectLayout.ObjectVisitor {
|
|
internal int Count;
|
|
|
|
internal void Initialize(bool isVisitedFlag) {
|
|
this.isVisitedFlag = isVisitedFlag;
|
|
this.Count = 0;
|
|
}
|
|
|
|
internal override unsafe UIntPtr Visit(Object obj) {
|
|
VTable vtable = obj.vtable;
|
|
RuntimeType rType = vtable.vtableType;
|
|
if (MultiUseWord.IsMarked(rType) != this.isVisitedFlag) {
|
|
this.Count++;
|
|
MultiUseWord.SetMark(rType, this.isVisitedFlag);
|
|
}
|
|
|
|
return ObjectLayout.ObjectSize(Magic.addressOf(obj), vtable);
|
|
}
|
|
|
|
private bool isVisitedFlag;
|
|
}
|
|
|
|
private class RuntimeTypeMapper : ObjectLayout.ObjectVisitor {
|
|
internal void Initialize(bool isVisitedFlag,
|
|
RuntimeTypeAccounting[] table) {
|
|
this.isVisitedFlag = isVisitedFlag;
|
|
this.tableIndex = 0;
|
|
this.accounts = table;
|
|
}
|
|
|
|
internal override unsafe UIntPtr Visit(Object obj) {
|
|
VTable vtable = obj.vtable;
|
|
RuntimeType rType = vtable.vtableType;
|
|
if (MultiUseWord.IsMarked(rType) != this.isVisitedFlag) {
|
|
VTable.Assert(this.tableIndex <
|
|
this.accounts.Length,
|
|
@"this.tableIndex <
|
|
this.accounts.Length");
|
|
|
|
this.accounts[this.tableIndex].RuntimeTypeObject =
|
|
rType;
|
|
this.accounts[this.tableIndex].TotalSize =
|
|
UIntPtr.Zero;
|
|
this.accounts[this.tableIndex].Count = 0;
|
|
MultiUseWord.SetMark(rType, this.isVisitedFlag);
|
|
this.tableIndex++;
|
|
}
|
|
|
|
return ObjectLayout.ObjectSize(Magic.addressOf(obj), vtable);
|
|
}
|
|
|
|
private bool isVisitedFlag;
|
|
private uint tableIndex;
|
|
private RuntimeTypeAccounting[] accounts;
|
|
}
|
|
|
|
private class InstanceReckoner : ObjectLayout.ObjectVisitor {
|
|
internal void Initialize(RuntimeTypeAccounting[] table) {
|
|
this.accounts = table;
|
|
}
|
|
|
|
internal override unsafe UIntPtr Visit(Object obj) {
|
|
VTable vtable = obj.vtable;
|
|
RuntimeType rType = vtable.vtableType;
|
|
uint tableIndex = (uint) MultiUseWord.GetValForObject(rType);
|
|
UIntPtr objAddr = Magic.addressOf(obj);
|
|
this.accounts[tableIndex].TotalSize +=
|
|
ObjectLayout.ObjectSize(objAddr, vtable);
|
|
this.accounts[tableIndex].Count++;
|
|
|
|
return ObjectLayout.ObjectSize(objAddr, vtable);
|
|
}
|
|
|
|
private RuntimeTypeAccounting[] accounts;
|
|
}
|
|
}
|
|
}
|