/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.mmtk.plan.refcount;
import org.mmtk.plan.StopTheWorld;
import org.mmtk.plan.Trace;
import org.mmtk.plan.refcount.cd.CD;
import org.mmtk.plan.refcount.cd.NullCD;
import org.mmtk.plan.refcount.cd.TrialDeletion;
import org.mmtk.policy.ExplicitFreeListSpace;
import org.mmtk.policy.ExplicitLargeObjectLocal;
import org.mmtk.policy.Space;
import org.mmtk.utility.deque.SharedDeque;
import org.mmtk.utility.heap.VMRequest;
import org.mmtk.utility.options.Options;
import org.mmtk.utility.statistics.EventCounter;
import org.mmtk.vm.VM;
import org.vmmagic.pragma.*;
import org.vmmagic.unboxed.*;
/**
* This class implements the global state of a simple reference counting
* collector.
*
* All plans make a clear distinction between <i>global</i> and
* <i>thread-local</i> activities, and divides global and local state
* into separate class hierarchies. Global activities must be
* synchronized, whereas no synchronization is required for
* thread-local activities. There is a single instance of Plan (or the
* appropriate sub-class), and a 1:1 mapping of PlanLocal to "kernel
* threads" (aka CPUs or in Jikes RVM, VM_Processors). Thus instance
* methods of PlanLocal allow fast, unsychronized access to functions such as
* allocation and collection.
*
* The global instance defines and manages static resources
* (such as memory and virtual memory resources). This mapping of threads to
* instances is crucial to understanding the correctness and
* performance properties of MMTk plans.
*/
@Uninterruptible public abstract class RCBase extends StopTheWorld {
/****************************************************************************
* Constants
*/
public static final boolean WITH_COALESCING_RC = true;
public static final boolean INC_DEC_ROOT = true;
public static final boolean INLINE_WRITE_BARRIER = WITH_COALESCING_RC;
public static final boolean GATHER_WRITE_BARRIER_STATS = false;
public static final boolean FORCE_FULL_CD = false;
// Cycle detection selection
public static final int NO_CYCLE_DETECTOR = 0;
public static final int TRIAL_DELETION = 1;
public static final int CYCLE_DETECTOR = TRIAL_DELETION;
/****************************************************************************
* Class variables
*/
public static final ExplicitFreeListSpace rcSpace = new ExplicitFreeListSpace("rc", DEFAULT_POLL_FREQUENCY, VMRequest.create(0.5f));
public static final int REF_COUNT = rcSpace.getDescriptor();
public static final ExplicitFreeListSpace smallCodeSpace = new ExplicitFreeListSpace("rc-sm-code", DEFAULT_POLL_FREQUENCY, VMRequest.create());
public static final int RC_SMALL_CODE = smallCodeSpace.getDescriptor();
// Counters
public static EventCounter wbFast;
public static EventCounter wbSlow;
// Allocators
public static final int ALLOC_RC = StopTheWorld.ALLOCATORS;
public static final int ALLOCATORS = ALLOC_RC + 1;
// Cycle Detectors
private NullCD nullCD;
private TrialDeletion trialDeletionCD;
/****************************************************************************
* Instance variables
*/
public final Trace rcTrace;
public final SharedDeque decPool;
public final SharedDeque modPool;
public final SharedDeque newRootPool;
public final SharedDeque oldRootPool;
protected int previousMetaDataPages;
/**
* Constructor.
*/
public RCBase() {
if (!SCAN_BOOT_IMAGE) {
VM.assertions.fail("RC currently requires scan boot image");
}
if (GATHER_WRITE_BARRIER_STATS) {
wbFast = new EventCounter("wbFast");
wbSlow = new EventCounter("wbSlow");
}
previousMetaDataPages = 0;
rcTrace = new Trace(metaDataSpace);
decPool = new SharedDeque("decPool",metaDataSpace, 1);
modPool = new SharedDeque("modPool",metaDataSpace, 1);
newRootPool = new SharedDeque("newRootPool",metaDataSpace, 1);
oldRootPool = new SharedDeque("oldRootPool",metaDataSpace, 1);
switch (RCBase.CYCLE_DETECTOR) {
case RCBase.NO_CYCLE_DETECTOR:
nullCD = new NullCD();
break;
case RCBase.TRIAL_DELETION:
trialDeletionCD = new TrialDeletion(this);
break;
}
}
/**
* Perform any required initialization of the GC portion of the header.
* Called for objects created at boot time.
*
* @param ref the object ref to the storage to be initialized
* @param typeRef the type reference for the instance being created
* @param size the number of bytes allocated by the GC system for
* this object.
* @param status the initial value of the status word
* @return The new value of the status word
*/
@Inline
public Word setBootTimeGCBits(Address ref, ObjectReference typeRef,
int size, Word status) {
if (WITH_COALESCING_RC) {
status = status.or(SCAN_BOOT_IMAGE ? RCHeader.LOGGED : RCHeader.UNLOGGED);
}
return status;
}
/*****************************************************************************
*
* Collection
*/
/**
* Perform a (global) collection phase.
*
* @param phaseId Collection phase to execute.
*/
@Inline
public void collectionPhase(short phaseId) {
if (phaseId == PREPARE) {
rcTrace.prepare();
modPool.prepareNonBlocking();
decPool.prepare();
oldRootPool.prepare();
newRootPool.prepare();
return;
}
if (phaseId == CLOSURE) {
rcTrace.prepare();
return;
}
if (phaseId == ROOTS) {
oldRootPool.reset();
super.collectionPhase(phaseId);
return;
}
if (phaseId == RELEASE) {
newRootPool.reset();
decPool.reset();
modPool.reset();
rcTrace.release();
previousMetaDataPages = metaDataSpace.reservedPages();
return;
}
if (!cycleDetector().collectionPhase(phaseId)) {
super.collectionPhase(phaseId);
}
}
/**
* This method controls the triggering of a GC. It is called periodically
* during allocation. Returns true to trigger a collection.
*
* @param spaceFull Space request failed, must recover pages within 'space'.
* @return True if a collection is requested by the plan.
*/
public boolean collectionRequired(boolean spaceFull) {
int newMetaDataPages = metaDataSpace.committedPages() - previousMetaDataPages;
return super.collectionRequired(spaceFull) || (newMetaDataPages > Options.metaDataLimit.getPages());
}
/*****************************************************************************
*
* Accounting
*/
/**
* Return the number of pages reserved for use given the pending
* allocation. The superclass accounts for its spaces, we just
* augment this with the mark-sweep space's contribution.
*
* @return The number of pages reserved given the pending
* allocation, excluding space reserved for copying.
*/
public int getPagesUsed() {
return (rcSpace.reservedPages() + super.getPagesUsed());
}
/**
* Calculate the number of pages a collection is required to free to satisfy
* outstanding allocation requests.
*
* @return the number of pages a collection is required to free to satisfy
* outstanding allocation requests.
*/
public int getPagesRequired() {
return super.getPagesRequired() + rcSpace.requiredPages();
}
/****************************************************************************
*
* Miscellaneous
*/
/**
* Determine if an object is in a reference counted space.
*
* @param object The object to check
* @return True if the object is in a reference counted space.
*/
public static boolean isRCObject(ObjectReference object) {
return !object.isNull() && !Space.isInSpace(VM_SPACE, object);
}
/**
* Free a reference counted object.
*
* @param object The object to free.
*/
public static void free(ObjectReference object) {
if (VM.VERIFY_ASSERTIONS) {
VM.assertions._assert(isRCObject(object));
}
if (Space.isInSpace(REF_COUNT, object)) {
ExplicitFreeListSpace.free(object);
} else if (Space.isInSpace(LOS, object)){
ExplicitLargeObjectLocal.free(loSpace, object);
} else if (Space.isInSpace(RC_SMALL_CODE, object)) {
ExplicitFreeListSpace.free(object);
} else if (Space.isInSpace(LARGE_CODE, object)) {
ExplicitLargeObjectLocal.free(largeCodeSpace, object);
}
}
/** @return The active cycle detector instance */
@Inline
public final CD cycleDetector() {
switch (RCBase.CYCLE_DETECTOR) {
case RCBase.NO_CYCLE_DETECTOR:
return nullCD;
case RCBase.TRIAL_DELETION:
return trialDeletionCD;
}
VM.assertions.fail("No cycle detector instance found.");
return null;
}
/**
* @see org.mmtk.plan.Plan#willNeverMove
*
* @param object Object in question
* @return True if the object will never move
*/
@Override
public boolean willNeverMove(ObjectReference object) {
if (Space.isInSpace(REF_COUNT, object))
return true;
else if (Space.isInSpace(RC_SMALL_CODE, object))
return true;
return super.willNeverMove(object);
}
}