795 lines
37 KiB
C#
795 lines
37 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 Microsoft.Bartok.Runtime;
|
|
using System.Collections;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Threading;
|
|
|
|
#if SINGULARITY_KERNEL
|
|
using Microsoft.Singularity;
|
|
#endif
|
|
|
|
[NoCCtor]
|
|
internal unsafe class SemispaceCollector: GenerationalCollector
|
|
{
|
|
|
|
// WARNING: don't initialize any static fields in this class!
|
|
|
|
internal static CopyScan[] copyScanners;
|
|
|
|
internal static SemispaceCollector instance;
|
|
|
|
// Visitor instances used for marking objects
|
|
private static ForwardReferenceVisitor generalReferenceVisitor;
|
|
private static ForwardThreadReference threadReferenceVisitor;
|
|
private static RegisterPinnedReference pinnedReferenceVisitor;
|
|
private static ForwardOnlyReferenceVisitor forwardOnlyReferenceVisitor;
|
|
|
|
private SemispaceCollector() {
|
|
}
|
|
|
|
public static new void Initialize() {
|
|
GenerationalCollector.Initialize();
|
|
// copyScanners = new CopyScan[MAX_GENERATION+1];
|
|
copyScanners = (CopyScan[])
|
|
BootstrapMemory.Allocate(typeof(CopyScan[]),
|
|
(uint)MAX_GENERATION+1);
|
|
for (int i = (int)MIN_GENERATION; i <= (int)MAX_GENERATION; i++) {
|
|
switch (GC.copyscanType) {
|
|
case CopyScanType.CheneyScan: {
|
|
copyScanners[i] = (CopyScan)
|
|
BootstrapMemory.Allocate(typeof(CheneyScan));
|
|
break;
|
|
}
|
|
case CopyScanType.HierarchicalScan: {
|
|
copyScanners[i] = (CopyScan)
|
|
BootstrapMemory.Allocate(typeof(HierarchicalScan));
|
|
break;
|
|
}
|
|
case CopyScanType.NestedHierarchicalScan: {
|
|
copyScanners[i] = (CopyScan)
|
|
BootstrapMemory.Allocate(typeof(NestedHierarchicalScan));
|
|
break;
|
|
}
|
|
default: {
|
|
VTable.NotReached("Unknown CopyScan type: "+
|
|
GC.copyscanType);
|
|
break;
|
|
}
|
|
}
|
|
copyScanners[i].Initialize((PageType) i);
|
|
}
|
|
// SemispaceCollector.instance = new SemispaceCollector();
|
|
SemispaceCollector.instance = (SemispaceCollector)
|
|
BootstrapMemory.Allocate(typeof(SemispaceCollector));
|
|
// generalReferenceVisitor = new ForwardReferenceVisitor();
|
|
SemispaceCollector.generalReferenceVisitor = (ForwardReferenceVisitor)
|
|
BootstrapMemory.Allocate(typeof(ForwardReferenceVisitor));
|
|
// threadReferenceVisitor = new ForwardThreadReference(generalReferenceVisitor);
|
|
SemispaceCollector.threadReferenceVisitor = (ForwardThreadReference)
|
|
BootstrapMemory.Allocate(typeof(ForwardThreadReference));
|
|
// pinnedReferenceVisitor = new RegisterPinnedReference();
|
|
SemispaceCollector.pinnedReferenceVisitor = (RegisterPinnedReference)
|
|
BootstrapMemory.Allocate(typeof(RegisterPinnedReference));
|
|
// forwardOnlyReferenceVisitor = new ForwardOnlyReferenceVisitor();
|
|
SemispaceCollector.forwardOnlyReferenceVisitor = (ForwardOnlyReferenceVisitor)
|
|
BootstrapMemory.Allocate(typeof(ForwardOnlyReferenceVisitor));
|
|
}
|
|
|
|
internal override void TruncateOlderAllocationAreas(int generation) {
|
|
for (int i = (int)MIN_GENERATION; i <= generation; i++) {
|
|
copyScanners[i].TruncateAllocationArea();
|
|
}
|
|
}
|
|
|
|
internal override void CollectGeneration(int generation,
|
|
UIntPtr generationPageCount)
|
|
{
|
|
UIntPtr fromSpacePageCountTotal = UIntPtr.Zero;
|
|
for (int i = MinGeneration; i <= generation; i++) {
|
|
fromSpacePageCountTotal += fromSpacePageCounts[i];
|
|
}
|
|
|
|
PageType maxDestGeneration = (generation < (int)MAX_GENERATION)
|
|
? (PageType)(generation+1)
|
|
: MAX_GENERATION;
|
|
|
|
// The real work: find the roots, copy reachable objects
|
|
CollectorStatistics.Event(GCEvent.ComputeRootSet);
|
|
#if !VC
|
|
TryAllManager.PreGCHookTryAll();
|
|
#endif
|
|
bool onlyNew = false;
|
|
if (generation == (int)nurseryGeneration) {
|
|
onlyNew = true;
|
|
}
|
|
MultiUseWord.PreGCHook(false, /* don't use shadows */
|
|
onlyNew /* only scan new EMUs */);
|
|
Finalizer.PrepareCollectFinalizers();
|
|
CallStack.ScanStacks(null, pinnedReferenceVisitor);
|
|
pinnedReferenceVisitor.ProcessPinnedPages(generalReferenceVisitor);
|
|
CallStack.ScanStacks(threadReferenceVisitor, null);
|
|
Thread.VisitBootstrapData(generalReferenceVisitor);
|
|
#if SINGULARITY
|
|
Thread.UpdateAfterGC();
|
|
#endif
|
|
Thread currentThread = Thread.threadTable[collectorThreadIndex];
|
|
#if SINGULARITY_KERNEL
|
|
Kernel.VisitSpecialData(generalReferenceVisitor);
|
|
Kernel.UpdateAfterGC(currentThread);
|
|
#endif
|
|
MultiUseWord.VisitStrongRefs(generalReferenceVisitor,
|
|
false /* Don't use shadows */);
|
|
#if !VC
|
|
TryAllManager.VisitStrongRefs(generalReferenceVisitor);
|
|
#endif
|
|
StaticData.ScanStaticData(generalReferenceVisitor);
|
|
if ((PageType)generation < MAX_GENERATION) {
|
|
installedRemSet.Clean();
|
|
this.ScanRemSet((PageType) generation);
|
|
}
|
|
installedRemSet.Reset();
|
|
CollectorStatistics.Event(GCEvent.TraceStart);
|
|
this.ScanAndCopy(maxDestGeneration);
|
|
CollectorStatistics.Event(GCEvent.TraceSpecial);
|
|
WeakReference.Process(forwardOnlyReferenceVisitor, true, true);
|
|
Finalizer.ResurrectCandidates(forwardOnlyReferenceVisitor,
|
|
generalReferenceVisitor,
|
|
true);
|
|
this.ScanAndCopy(maxDestGeneration);
|
|
WeakReference.Process(forwardOnlyReferenceVisitor, true, false);
|
|
MultiUseWord.VisitWeakRefs(forwardOnlyReferenceVisitor,
|
|
false /* Don't use shadows */);
|
|
#if !VC
|
|
TryAllManager.VisitWeakRefs(forwardOnlyReferenceVisitor);
|
|
#endif
|
|
CollectorStatistics.Event(GCEvent.CollectionComplete);
|
|
// Clean up the auxiliary data structures
|
|
for (int i = MinGeneration + 1; i <= (int)maxDestGeneration; i++) {
|
|
copyScanners[i].Cleanup();
|
|
}
|
|
UnmanagedPageList.ReleaseStandbyPages();
|
|
pinnedReferenceVisitor.CleanPinnedPages();
|
|
MultiUseWord.PostGCHook(onlyNew);
|
|
Finalizer.ReleaseCollectFinalizers();
|
|
#if !VC
|
|
TryAllManager.PostGCHookTryAll();
|
|
#endif
|
|
}
|
|
|
|
internal override void EnableHeap() {
|
|
// Ensure that we don't call any type initializers during a GC
|
|
ArrayList pageList = new ArrayList();
|
|
pageList.Add(new UIntPtr(11));
|
|
pageList.Add(new UIntPtr(10));
|
|
UIntPtrComparer comparer = new UIntPtrComparer();
|
|
pageList.Sort(comparer);
|
|
}
|
|
|
|
// Internal workings of the collector
|
|
|
|
// Scan is large and will be inlined here. This is a performance
|
|
// workaround that performs manual specialization of the Scan method.
|
|
[NoInline]
|
|
private void ScanRemSet(PageType generation) {
|
|
installedRemSet.Scan(generalReferenceVisitor, generation);
|
|
}
|
|
|
|
|
|
private void ScanAndCopy(PageType maxDestGeneration) {
|
|
bool repeat = true;
|
|
while (repeat) {
|
|
repeat = false;
|
|
for (int gen = MinGeneration; gen <= (int)maxDestGeneration; gen++) {
|
|
if (copyScanners[gen].Scan(generalReferenceVisitor)) {
|
|
repeat = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// No objects can be resurrected using this mechanism
|
|
private class ForwardOnlyReferenceVisitor : NonNullReferenceVisitor
|
|
{
|
|
|
|
internal unsafe override void Visit(UIntPtr *loc) {
|
|
UIntPtr addr = *loc;
|
|
UIntPtr page = PageTable.Page(addr);
|
|
PageType pageType = PageTable.Type(page);
|
|
if (!PageTable.IsZombiePage(pageType)) {
|
|
VTable.Assert(PageTable.IsGcPage(pageType) ||
|
|
PageTable.IsNonGcPage(pageType) ||
|
|
PageTable.IsStackPage(pageType) ||
|
|
PageTable.IsSharedPage(pageType),
|
|
"Semispace:ForwardOnlyReferenceVisitor");
|
|
return;
|
|
}
|
|
PageType gen = PageTable.ZombieToLive(pageType);
|
|
UIntPtr vtableAddr = Allocator.GetObjectVTable(addr);
|
|
UIntPtr vtablePageIndex = PageTable.Page(vtableAddr);
|
|
if (PageTable.Type(vtablePageIndex) == gen) {
|
|
// The vtable field is really a forwarding pointer
|
|
*loc = vtableAddr;
|
|
} else {
|
|
// The object was not live
|
|
*loc = UIntPtr.Zero;
|
|
}
|
|
}
|
|
|
|
#region HELP_DEVIRT
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[ManualRefCounts]
|
|
internal override UIntPtr VisitReferenceFields(Object obj)
|
|
{
|
|
return this.VisitReferenceFields(Magic.addressOf(obj),
|
|
obj.vtable);
|
|
}
|
|
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[ManualRefCounts]
|
|
[InlineIntoOnce]
|
|
internal override
|
|
UIntPtr VisitReferenceFields(UIntPtr objectBase, VTable vtable)
|
|
{
|
|
ObjectDescriptor objDesc =
|
|
new ObjectDescriptor(vtable, objectBase);
|
|
return VisitReferenceFieldsTemplate(ref objDesc);
|
|
}
|
|
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[Inline]
|
|
protected override unsafe sealed
|
|
void Filter(UIntPtr *location, ref ObjectDescriptor objDesc)
|
|
{
|
|
base.Filter(location, ref objDesc);
|
|
}
|
|
#endregion
|
|
|
|
}
|
|
|
|
internal class ForwardReferenceVisitor : NonNullReferenceVisitor
|
|
{
|
|
|
|
internal unsafe override void Visit(UIntPtr *loc) {
|
|
UIntPtr objAddr = *loc;
|
|
UIntPtr page = PageTable.Page(objAddr);
|
|
Trace.Log(Trace.Area.Pointer,
|
|
"FwdRef: loc={0}, addr={1}, page={2}",
|
|
__arglist(loc, objAddr, page));
|
|
PageType pageType = PageTable.Type(page);
|
|
if (!PageTable.IsZombiePage(pageType)) {
|
|
VTable.Assert(PageTable.IsGcPage(pageType) ||
|
|
PageTable.IsNonGcPage(pageType) ||
|
|
PageTable.IsStackPage(pageType) ||
|
|
PageTable.IsSharedPage(pageType),
|
|
"Semispace:ForwardReferenceVisitor");
|
|
return;
|
|
}
|
|
PageType gen = PageTable.ZombieToLive(pageType);
|
|
VTable.Assert(gen > MIN_GENERATION);
|
|
Object obj = Magic.fromAddress(objAddr);
|
|
UIntPtr vtableAddr = *obj.VTableFieldAddr;
|
|
UIntPtr vtablePageIndex = PageTable.Page(vtableAddr);
|
|
if (PageTable.Type(vtablePageIndex) == gen) {
|
|
// The vtable field is really a forwarding pointer
|
|
Trace.Log(Trace.Area.Pointer,
|
|
"FwdRef: VT fwd: {0} -> {1}",
|
|
__arglist(objAddr, vtableAddr));
|
|
*loc = vtableAddr;
|
|
return;
|
|
}
|
|
Object newObject = copyScanners[(int)gen].Copy(obj);
|
|
*loc = Magic.addressOf(newObject);
|
|
}
|
|
|
|
#region HELP_DEVIRT
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[ManualRefCounts]
|
|
internal override UIntPtr VisitReferenceFields(Object obj)
|
|
{
|
|
return this.VisitReferenceFields(Magic.addressOf(obj),
|
|
obj.vtable);
|
|
}
|
|
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[ManualRefCounts]
|
|
[InlineIntoOnce]
|
|
internal override
|
|
UIntPtr VisitReferenceFields(UIntPtr objectBase, VTable vtable)
|
|
{
|
|
ObjectDescriptor objDesc =
|
|
new ObjectDescriptor(vtable, objectBase);
|
|
return VisitReferenceFieldsTemplate(ref objDesc);
|
|
}
|
|
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[Inline]
|
|
protected override unsafe sealed
|
|
void Filter(UIntPtr *location, ref ObjectDescriptor objDesc)
|
|
{
|
|
base.Filter(location, ref objDesc);
|
|
}
|
|
#endregion
|
|
|
|
}
|
|
|
|
private class ForwardThreadReference : NonNullReferenceVisitor
|
|
{
|
|
|
|
internal unsafe override void Visit(UIntPtr *loc) {
|
|
UIntPtr addr = *loc;
|
|
UIntPtr page = PageTable.Page(addr);
|
|
Trace.Log(Trace.Area.Pointer,
|
|
"FwdThrRef: loc={0}, addr={1}, page={2}",
|
|
__arglist(loc, addr, page));
|
|
PageType pageType = PageTable.Type(page);
|
|
// if an object "spills" into a page that
|
|
// is pinned, and the object is copied
|
|
// during a collection, we will end up with
|
|
// the first part of the object in a zombie page
|
|
// the second part of the object in a GC page.
|
|
// We need to find the start of the object and
|
|
// use that to determine whether the object has
|
|
// been moved.
|
|
if (!PageTable.IsZombiePage(pageType) &&
|
|
!PageTable.IsGcPage(pageType)) {
|
|
VTable.Assert(PageTable.IsNonGcPage(pageType) ||
|
|
PageTable.IsStackPage(pageType) ||
|
|
PageTable.IsSharedPage(pageType) ||
|
|
VTable.BuildC2Mods,
|
|
"Semispace:ForwardThreadReference");
|
|
return;
|
|
}
|
|
|
|
UIntPtr objectPtr = InteriorPtrTable.Find(addr);
|
|
if (objectPtr == addr) {
|
|
generalReferenceVisitor.Visit(loc);
|
|
} else {
|
|
// we can check for the page type of
|
|
// objectPtr here to see if it is zombie page.
|
|
// If true we can just return.
|
|
UIntPtr newObject = objectPtr;
|
|
generalReferenceVisitor.Visit(&newObject);
|
|
UIntPtr newAddr = newObject + (addr - objectPtr);
|
|
Trace.Log(Trace.Area.Pointer,
|
|
"FwdThrRef: {0} -> {1}",
|
|
__arglist(addr, newAddr));
|
|
*loc = newAddr;
|
|
}
|
|
}
|
|
|
|
#region HELP_DEVIRT
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[ManualRefCounts]
|
|
internal override UIntPtr VisitReferenceFields(Object obj)
|
|
{
|
|
return this.VisitReferenceFields(Magic.addressOf(obj),
|
|
obj.vtable);
|
|
}
|
|
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[ManualRefCounts]
|
|
[InlineIntoOnce]
|
|
internal override
|
|
UIntPtr VisitReferenceFields(UIntPtr objectBase, VTable vtable)
|
|
{
|
|
ObjectDescriptor objDesc =
|
|
new ObjectDescriptor(vtable, objectBase);
|
|
return VisitReferenceFieldsTemplate(ref objDesc);
|
|
}
|
|
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[Inline]
|
|
protected override unsafe sealed
|
|
void Filter(UIntPtr *location, ref ObjectDescriptor objDesc)
|
|
{
|
|
base.Filter(location, ref objDesc);
|
|
}
|
|
#endregion
|
|
|
|
internal ForwardReferenceVisitor ptrVisitor;
|
|
|
|
}
|
|
|
|
private class RegisterPinnedReference : NonNullReferenceVisitor
|
|
{
|
|
|
|
// BUGBUG: We are allocating an ArrayList while the collector
|
|
// is running. If the ArrayList gets big enough to be
|
|
// allocated in the older generation, then the RemSet has the
|
|
// potential to overflow since the boxed integers will reside
|
|
// in the young generation. We should eventually eliminate
|
|
// the use of ArrayList in this class as well as avoid boxing
|
|
// the page indices.
|
|
|
|
internal unsafe override void Visit(UIntPtr *loc) {
|
|
UIntPtr addr = *loc;
|
|
UIntPtr page = PageTable.Page(addr);
|
|
PageType pageType = PageTable.Type(page);
|
|
if (!PageTable.IsZombiePage(pageType)) {
|
|
VTable.Assert(PageTable.IsGcPage(pageType) ||
|
|
PageTable.IsNonGcPage(pageType) ||
|
|
PageTable.IsStackPage(pageType) ||
|
|
PageTable.IsSharedPage(pageType) ||
|
|
VTable.BuildC2Mods,
|
|
"Semispace:RegisterPinnedReference:1");
|
|
return;
|
|
}
|
|
PageType gen = PageTable.ZombieToLive(pageType);
|
|
UIntPtr pinnedObjectAddr = InteriorPtrTable.Find(addr);
|
|
if (pinnedPageList == null) {
|
|
pinnedPageList = new ArrayList();
|
|
comparer = new UIntPtrComparer();
|
|
}
|
|
Object pinnedObject = Magic.fromAddress(pinnedObjectAddr);
|
|
UIntPtr objectSize =
|
|
ObjectLayout.ObjectSize(pinnedObjectAddr,
|
|
pinnedObject.vtable);
|
|
UIntPtr beforeObjectAddr = pinnedObjectAddr - PreHeader.Size;
|
|
UIntPtr pastObjectAddr = beforeObjectAddr + objectSize;
|
|
UIntPtr firstPage = PageTable.Page(beforeObjectAddr);
|
|
UIntPtr lastPage = PageTable.Page(pastObjectAddr - 1);
|
|
for (UIntPtr i = firstPage; i <= lastPage; i++) {
|
|
if (!pinnedPageList.Contains(i)) {
|
|
Trace.Log(Trace.Area.Pointer,
|
|
"RegPin: ptr={0} page={1} gen={2}",
|
|
__arglist(pinnedObjectAddr, i, gen));
|
|
GenerationalCollector.gcPromotedTable[(int) gen-1] +=
|
|
PageTable.PageSize;
|
|
pinnedPageList.Add(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
#region HELP_DEVIRT
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[ManualRefCounts]
|
|
internal override UIntPtr VisitReferenceFields(Object obj)
|
|
{
|
|
return this.VisitReferenceFields(Magic.addressOf(obj),
|
|
obj.vtable);
|
|
}
|
|
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[ManualRefCounts]
|
|
[InlineIntoOnce]
|
|
internal override
|
|
UIntPtr VisitReferenceFields(UIntPtr objectBase, VTable vtable)
|
|
{
|
|
ObjectDescriptor objDesc =
|
|
new ObjectDescriptor(vtable, objectBase);
|
|
return VisitReferenceFieldsTemplate(ref objDesc);
|
|
}
|
|
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[Inline]
|
|
protected override unsafe sealed
|
|
void Filter(UIntPtr *location, ref ObjectDescriptor objDesc)
|
|
{
|
|
base.Filter(location, ref objDesc);
|
|
}
|
|
#endregion
|
|
|
|
internal void ProcessPinnedPages(ReferenceVisitor ptrVisitor) {
|
|
if (pinnedPageList == null || pinnedPageList.Count == 0) {
|
|
return;
|
|
}
|
|
pinnedPageList.Sort(comparer);
|
|
int limit = pinnedPageList.Count;
|
|
for (int i = 0; i < limit; i++) {
|
|
UIntPtr page = (UIntPtr) pinnedPageList[i];
|
|
PageType fromSpaceType = PageTable.Type(page);
|
|
VTable.Assert(PageTable.IsZombiePage(fromSpaceType),
|
|
"Semispace:RegisterPinnedReference:2");
|
|
PageType toSpaceType =
|
|
PageTable.ZombieToLive(fromSpaceType);
|
|
PageTable.SetType(page, toSpaceType);
|
|
}
|
|
int pageIndex = 0;
|
|
while (pageIndex < limit) {
|
|
UIntPtr startPage = (UIntPtr) pinnedPageList[pageIndex];
|
|
UIntPtr endPage = startPage + 1;
|
|
pageIndex++;
|
|
while (pageIndex < limit &&
|
|
(UIntPtr) pinnedPageList[pageIndex] == endPage) {
|
|
pageIndex++;
|
|
endPage++;
|
|
}
|
|
UIntPtr objectAddr = FirstPinnedObjectAddr(startPage);
|
|
UIntPtr pastAddr = PostPinnedObjectAddr(endPage);
|
|
while (objectAddr < pastAddr) {
|
|
if (Allocator.IsAlignment(objectAddr)) {
|
|
objectAddr += UIntPtr.Size;
|
|
} else if (BumpAllocator.IsUnusedSpace(objectAddr)) {
|
|
objectAddr = (PageTable.PagePad(objectAddr) +
|
|
PreHeader.Size);
|
|
} else {
|
|
Object obj = Magic.fromAddress(objectAddr);
|
|
objectAddr += ptrVisitor.VisitReferenceFields(obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void CleanPinnedPages() {
|
|
if (pinnedPageList == null || pinnedPageList.Count == 0) {
|
|
return;
|
|
}
|
|
int pageIndex = 0;
|
|
int limit = pinnedPageList.Count;
|
|
UIntPtr lastPostPinnedAddr = UIntPtr.Zero;
|
|
while (pageIndex < limit) {
|
|
UIntPtr startPage = (UIntPtr) pinnedPageList[pageIndex];
|
|
UIntPtr endPage = startPage + 1;
|
|
pageIndex++;
|
|
while (pageIndex < limit &&
|
|
(UIntPtr) pinnedPageList[pageIndex] == endPage) {
|
|
pageIndex++;
|
|
endPage++;
|
|
}
|
|
// Zero out the area between the start of the page and
|
|
// the first object on the page
|
|
UIntPtr firstObjectAddr = FirstPinnedObjectAddr(startPage);
|
|
UIntPtr firstAddr = firstObjectAddr - PreHeader.Size;
|
|
UIntPtr trashAddr = PageTable.PageAddr(startPage);
|
|
if (firstAddr < trashAddr) {
|
|
// The first object "spills" into the previous page,
|
|
// presumably by no more than HEADER_BYTES bytes
|
|
VTable.Assert(
|
|
PageTable.Page(firstAddr) == startPage - 1,
|
|
"Semispace:RegisterPinnedReference:3");
|
|
// Prepare to zero the preceding page unless it also
|
|
// had pinned data on it
|
|
trashAddr = PageTable.PageAddr(startPage - 1);
|
|
InteriorPtrTable.ClearFirst(startPage - 1);
|
|
if (trashAddr >= lastPostPinnedAddr) {
|
|
// Need to mark the spilled-onto page live to
|
|
// keep the spilled data around
|
|
PageType fromSpaceType =
|
|
PageTable.Type(startPage - 1);
|
|
VTable.Assert(
|
|
PageTable.IsZombiePage(fromSpaceType),
|
|
"Semispace:RegisterPinnedReference:4");
|
|
PageType toSpaceType =
|
|
PageTable.ZombieToLive(fromSpaceType);
|
|
PageTable.SetType(startPage - 1, toSpaceType);
|
|
}
|
|
}
|
|
// If lastPostPinnedAddr is on the page that trashAddr
|
|
// starts, pinned data from the last run of pinned pages
|
|
// and pinned data from this run of pinned data are on the
|
|
// same page, so just write alignment tokens from
|
|
// lastPostPinnedAddr to the first pinned object.
|
|
// Otherwise, write an unused marker at lastPostPinnedAddr
|
|
// since the rest of its page must be copied or dead.
|
|
if (trashAddr < lastPostPinnedAddr) {
|
|
trashAddr = lastPostPinnedAddr;
|
|
} else {
|
|
CleanPageTail(lastPostPinnedAddr);
|
|
}
|
|
|
|
if (GC.remsetType == RemSetType.Cards &&
|
|
trashAddr < firstAddr) {
|
|
UIntPtr firstCard = CardTable.CardNo(trashAddr);
|
|
UIntPtr lastCard = CardTable.CardNo(firstAddr -1);
|
|
|
|
if (!OffsetTable.NoObjectPtrToTheCard(firstCard)) {
|
|
UIntPtr offset= OffsetTable.GetOffset(firstCard);
|
|
UIntPtr objPtr =
|
|
CardTable.CardAddr(firstCard) + offset;
|
|
UIntPtr size = OffsetTable.ObjectSize(objPtr);
|
|
VTable.Assert
|
|
((objPtr + size - PreHeader.Size <= trashAddr)
|
|
|| (objPtr >= trashAddr),
|
|
"Object should be totally "
|
|
+ "above or below trashAddr");
|
|
if (objPtr >= trashAddr) {
|
|
// The offset in this card needs to be updated
|
|
OffsetTable.ClearCards(firstCard, firstCard);
|
|
}
|
|
}
|
|
|
|
OffsetTable.ClearCards(firstCard+1, lastCard-1);
|
|
|
|
if (lastCard != CardTable.CardNo(firstObjectAddr)) {
|
|
OffsetTable.ClearCards(lastCard, lastCard);
|
|
} else {
|
|
VTable.Assert(OffsetTable.GetOffset(lastCard)
|
|
>= (firstObjectAddr
|
|
- CardTable.CardAddr(lastCard)),
|
|
"wrong offset");
|
|
}
|
|
}
|
|
|
|
{
|
|
// trashAddr should go back at most one page.
|
|
|
|
UIntPtr trashPage = PageTable.Page(trashAddr);
|
|
UIntPtr firstObjectAddrPage =
|
|
PageTable.Page(firstObjectAddr);
|
|
VTable.Assert((trashPage == firstObjectAddrPage - 1)
|
|
|| (trashPage == firstObjectAddrPage));
|
|
}
|
|
|
|
// If the InteriorPtrTable already had a value, then this is
|
|
// redundant, but if the call to First above has to compute
|
|
// the value, then (since it won't store it in the table) we
|
|
// should store it. Why? At this point the previous page
|
|
// would be "connected" to this one. After this collection
|
|
// the previous page will be unused or re-used and unrelated
|
|
// to this page and subsequent calls to First would then
|
|
// rely on it making the leap between unrelated pages.
|
|
InteriorPtrTable.SetFirst(firstObjectAddr);
|
|
|
|
while (trashAddr < firstAddr) {
|
|
Allocator.WriteAlignment(trashAddr);
|
|
trashAddr += UIntPtr.Size;
|
|
}
|
|
|
|
// Zero out the area between the last whole object on
|
|
// the last page and the end of the last page
|
|
UIntPtr pastAddr = PostPinnedObjectAddr(endPage);
|
|
UIntPtr newEndPage =
|
|
PageTable.Page(PageTable.PagePad(pastAddr));
|
|
while (endPage < newEndPage) {
|
|
// The last object spills into the next page(s), so
|
|
// mark those page(s) live
|
|
PageType fromPageType = PageTable.Type(endPage);
|
|
if (PageTable.IsZombiePage(fromPageType)) {
|
|
PageType toSpaceType =
|
|
PageTable.ZombieToLive(fromPageType);
|
|
PageTable.SetType(endPage, toSpaceType);
|
|
} else {
|
|
// final page might be live already because
|
|
// something else on it was pinned.
|
|
// pageIndex has already been incremented,
|
|
// so it points to the start of the next
|
|
// set of contiguous pages
|
|
VTable.Assert(
|
|
PageTable.IsLiveGcPage(fromPageType) &&
|
|
pageIndex < limit &&
|
|
endPage ==
|
|
(UIntPtr) pinnedPageList[pageIndex],
|
|
"Semispace:RegisterPinnedReference:5");
|
|
}
|
|
++endPage;
|
|
}
|
|
lastPostPinnedAddr = pastAddr;
|
|
}
|
|
CleanPageTail(lastPostPinnedAddr);
|
|
pinnedPageList = null;
|
|
comparer = null;
|
|
}
|
|
|
|
private static void CleanPageTail(UIntPtr postPinnedAddr) {
|
|
if (!PageTable.PageAligned(postPinnedAddr)) {
|
|
// If postPinnedAddr points to the first object on its page,
|
|
// then we are removing all objects (specifically the part
|
|
// of the object that the InteriorPtrTable tracks, the
|
|
// vtables) from the page, so we should clear the page's
|
|
// entry in the InteriorPtrTable.
|
|
|
|
UIntPtr page = PageTable.Page(postPinnedAddr);
|
|
UIntPtr firstObjPtr = InteriorPtrTable.First(page);
|
|
if (firstObjPtr > postPinnedAddr) {
|
|
VTable.Assert
|
|
(firstObjPtr - PreHeader.Size >= postPinnedAddr,
|
|
"postPinnedAddr should not point to the "
|
|
+ "interior of an object (1)");
|
|
InteriorPtrTable.ClearFirst(page);
|
|
} else if (!BumpAllocator.IsUnusedSpace(firstObjPtr)) {
|
|
UIntPtr firstObjSize =
|
|
InteriorPtrTable.ObjectSize(firstObjPtr);
|
|
VTable.Assert
|
|
(firstObjPtr + firstObjSize - PreHeader.Size
|
|
<= postPinnedAddr,
|
|
"postPinnedAddr should not point to the "
|
|
+ "interior of an object (2)");
|
|
}
|
|
|
|
UIntPtr byteCount = PageTable.PagePad(postPinnedAddr)
|
|
- postPinnedAddr;
|
|
Util.MemClear(postPinnedAddr, byteCount);
|
|
BumpAllocator.WriteUnusedMarker(postPinnedAddr);
|
|
|
|
if (GC.remsetType == RemSetType.Cards && byteCount > 0) {
|
|
UIntPtr firstCard = CardTable.CardNo(postPinnedAddr);
|
|
UIntPtr lastCard =
|
|
CardTable.CardNo(postPinnedAddr + byteCount -1);
|
|
|
|
if (!OffsetTable.NoObjectPtrToTheCard(firstCard)) {
|
|
UIntPtr offset = OffsetTable.GetOffset(firstCard);
|
|
UIntPtr objPtr =
|
|
CardTable.CardAddr(firstCard) + offset;
|
|
UIntPtr size = OffsetTable.ObjectSize(objPtr);
|
|
|
|
VTable.Assert
|
|
((objPtr + size - PreHeader.Size
|
|
<= postPinnedAddr)
|
|
|| (objPtr >= postPinnedAddr),
|
|
"Object should be totally "
|
|
+ "above or below postPinnedAddr");
|
|
if (objPtr >= postPinnedAddr) {
|
|
OffsetTable.ClearCards(firstCard, firstCard);
|
|
}
|
|
}
|
|
|
|
OffsetTable.ClearCards(firstCard + 1, lastCard);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static UIntPtr FirstPinnedObjectAddr(UIntPtr startPage) {
|
|
return InteriorPtrTable.First(startPage);
|
|
}
|
|
|
|
private static UIntPtr PostPinnedObjectAddr(UIntPtr endPage) {
|
|
UIntPtr endAddr = PageTable.PageAddr(endPage);
|
|
UIntPtr postLastObjectAddr = InteriorPtrTable.Last(endPage-1);
|
|
if (postLastObjectAddr < endAddr &&
|
|
!BumpAllocator.IsUnusedSpace(postLastObjectAddr)) {
|
|
// If the next object straddles into the next page,
|
|
// return the location just past the object
|
|
Object lastObject = Magic.fromAddress(postLastObjectAddr);
|
|
UIntPtr lastObjectSize =
|
|
ObjectLayout.ObjectSize(postLastObjectAddr,
|
|
lastObject.vtable);
|
|
postLastObjectAddr += lastObjectSize;
|
|
}
|
|
return postLastObjectAddr - PreHeader.Size;
|
|
}
|
|
|
|
internal ArrayList pinnedPageList;
|
|
internal UIntPtrComparer comparer; // type defined in UIntPtr.cs
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// N.B. Must get two UIntPtrs!
|
|
|
|
internal class UIntPtrComparer : Collections.IComparer
|
|
{
|
|
public int Compare(Object x, Object y)
|
|
{
|
|
VTable.Assert(x is UIntPtr);
|
|
VTable.Assert(y is UIntPtr);
|
|
|
|
UIntPtr u = (UIntPtr) x;
|
|
UIntPtr v = (UIntPtr) y;
|
|
|
|
if(u < v) {
|
|
return -1;
|
|
}
|
|
if(u > v) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
}
|