blob: b2f10a952149e7b1c36ecbaadd5b759b68de3ba4 [file] [log] [blame]
/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* 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/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.mmtk.plan;
import org.mmtk.policy.MarkSweepSpace;
import org.mmtk.policy.Space;
import org.mmtk.policy.ImmortalSpace;
import org.mmtk.policy.RawPageSpace;
import org.mmtk.policy.LargeObjectSpace;
import org.mmtk.utility.alloc.LinearScan;
import org.mmtk.utility.Constants;
import org.mmtk.utility.Conversions;
import org.mmtk.utility.heap.HeapGrowthManager;
import org.mmtk.utility.heap.Map;
import org.mmtk.utility.heap.VMRequest;
import org.mmtk.utility.Log;
import org.mmtk.utility.options.*;
import org.mmtk.utility.sanitychecker.SanityChecker;
import org.mmtk.utility.statistics.PerfCounter;
import org.mmtk.utility.statistics.Timer;
import org.mmtk.utility.statistics.Stats;
import org.mmtk.vm.VM;
import org.mmtk.vm.Collection;
import org.vmmagic.pragma.*;
import org.vmmagic.unboxed.*;
/**
* This abstract class implements the global core functionality for all
* memory management schemes. All global MMTk plans should inherit from
* this class.<p>
*
* 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). Thus instance
* methods of PlanLocal allow fast, unsynchronized 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 Plan implements Constants {
/****************************************************************************
* Constants
*/
/* GC State */
public static final int NOT_IN_GC = 0; // this must be zero for C code
public static final int GC_PREPARE = 1; // before setup and obtaining root
public static final int GC_PROPER = 2;
/* Polling */
public static final int DEFAULT_POLL_FREQUENCY = (128 << 10) >> LOG_BYTES_IN_PAGE;
public static final int META_DATA_POLL_FREQUENCY = DEFAULT_POLL_FREQUENCY;
/* Space Size Constants. */
public static final boolean USE_CODE_SPACE = true;
public static final float PLOS_FRAC = 0.07f;
public static final int HEAP_FULL_MINIMUM = (1 << 17) >> LOG_BYTES_IN_PAGE; // 128K
public static final int HEAP_FULL_PERCENTAGE = 2;
/* Allocator Constants */
public static final int ALLOC_DEFAULT = 0;
public static final int ALLOC_NON_REFERENCE = 1;
public static final int ALLOC_NON_MOVING = 2;
public static final int ALLOC_IMMORTAL = 3;
public static final int ALLOC_LOS = 4;
public static final int ALLOC_PRIMITIVE_LOS = 5;
public static final int ALLOC_GCSPY = 6;
public static final int ALLOC_CODE = 7;
public static final int ALLOC_LARGE_CODE = 8;
public static final int ALLOC_HOT_CODE = USE_CODE_SPACE ? ALLOC_CODE : ALLOC_DEFAULT;
public static final int ALLOC_COLD_CODE = USE_CODE_SPACE ? ALLOC_CODE : ALLOC_DEFAULT;
public static final int ALLOC_STACK = ALLOC_LOS;
public static final int ALLOCATORS = 9;
public static final int DEFAULT_SITE = -1;
/* Miscellaneous Constants */
// public static final int LOS_SIZE_THRESHOLD = SegregatedFreeListSpace.MAX_CELL_SIZE;
public static final int NON_PARTICIPANT = 0;
public static final boolean GATHER_WRITE_BARRIER_STATS = false;
public static final int DEFAULT_MIN_NURSERY = (256 * 1024) >> LOG_BYTES_IN_PAGE;
public static final int DEFAULT_MAX_NURSERY = (32 << 20) >> LOG_BYTES_IN_PAGE;
public static final boolean SCAN_BOOT_IMAGE = true; // scan it for roots rather than trace it
public static final int MAX_COLLECTION_ATTEMPTS = 10;
// public static final boolean REQUIRES_LOS = VM.activePlan.constraints().requiresLOS();
public static final int MAX_NON_LOS_DEFAULT_ALLOC_BYTES = VM.activePlan.constraints().maxNonLOSDefaultAllocBytes();
public static final int MAX_NON_LOS_NONMOVING_ALLOC_BYTES = VM.activePlan.constraints().maxNonLOSNonMovingAllocBytes();
public static final int MAX_NON_LOS_COPY_BYTES = VM.activePlan.constraints().maxNonLOSCopyBytes();
/* Do we support a log bit in the object header? Some write barriers may use it */
public static final boolean NEEDS_LOG_BIT_IN_HEADER = VM.activePlan.constraints().needsLogBitInHeader();
public static final Word LOG_SET_MASK = VM.activePlan.constraints().unloggedBit();
private static final Word LOG_CLEAR_MASK = LOG_SET_MASK.not();
public static final Word UNLOGGED_BIT = VM.activePlan.constraints().unloggedBit();
/****************************************************************************
* Class variables
*/
/** The space that holds any VM specific objects (e.g. a boot image) */
public static final Space vmSpace = VM.memory.getVMSpace();
/** Any immortal objects allocated after booting are allocated here. */
public static final ImmortalSpace immortalSpace = new ImmortalSpace("immortal", DEFAULT_POLL_FREQUENCY, VMRequest.create());
/** All meta data that is used by MMTk is allocated (and accounted for) in the meta data space. */
public static final RawPageSpace metaDataSpace = new RawPageSpace("meta", DEFAULT_POLL_FREQUENCY, VMRequest.create());
/** Large objects are allocated into a special large object space. */
public static final LargeObjectSpace loSpace = new LargeObjectSpace("los", DEFAULT_POLL_FREQUENCY, VMRequest.create());
/** Space used by the sanity checker (used at runtime only if sanity checking enabled */
public static final RawPageSpace sanitySpace = new RawPageSpace("sanity", Integer.MAX_VALUE, VMRequest.create());
/** Space used to allocate objects that cannot be moved. we do not need a large space as the LOS is non-moving. */
public static final MarkSweepSpace nonMovingSpace = new MarkSweepSpace("non-moving", DEFAULT_POLL_FREQUENCY, VMRequest.create());
public static final MarkSweepSpace smallCodeSpace = USE_CODE_SPACE ? new MarkSweepSpace("sm-code", DEFAULT_POLL_FREQUENCY, VMRequest.create()) : null;
public static final LargeObjectSpace largeCodeSpace = USE_CODE_SPACE ? new LargeObjectSpace("lg-code", DEFAULT_POLL_FREQUENCY, VMRequest.create()) : null;
/* Space descriptors */
public static final int IMMORTAL = immortalSpace.getDescriptor();
public static final int VM_SPACE = vmSpace.getDescriptor();
public static final int META = metaDataSpace.getDescriptor();
public static final int LOS = loSpace.getDescriptor();
public static final int SANITY = sanitySpace.getDescriptor();
public static final int NON_MOVING = nonMovingSpace.getDescriptor();
public static final int SMALL_CODE = USE_CODE_SPACE ? smallCodeSpace.getDescriptor() : 0;
public static final int LARGE_CODE = USE_CODE_SPACE ? largeCodeSpace.getDescriptor() : 0;
/** Timer that counts total time */
public static final Timer totalTime = new Timer("time");
/** Performance counters */
public static final PerfCounter totalPerfCnt = new PerfCounter("perf");
/** Support for time-limited GCs */
protected static long timeCap;
/** Support for allocation-site identification */
protected static int allocationSiteCount = 0;
/** Global sanity checking state **/
public static final SanityChecker sanityChecker = new SanityChecker();
/****************************************************************************
* Constructor.
*/
public Plan() {
/* Create base option instances */
Options.verbose = new Verbose();
Options.verboseTiming = new VerboseTiming();
Options.stressFactor = new StressFactor();
Options.noFinalizer = new NoFinalizer();
Options.noReferenceTypes = new NoReferenceTypes();
Options.fullHeapSystemGC = new FullHeapSystemGC();
Options.harnessAll = new HarnessAll();
Options.ignoreSystemGC = new IgnoreSystemGC();
Options.metaDataLimit = new MetaDataLimit();
Options.nurserySize = new NurserySize();
Options.variableSizeHeap = new VariableSizeHeap();
Options.eagerMmapSpaces = new EagerMmapSpaces();
Options.sanityCheck = new SanityCheck();
Options.debugAddress = new DebugAddress();
Options.perfMetric = new PerfMetric();
Map.finalizeStaticSpaceMap();
registerSpecializedMethods();
}
/****************************************************************************
* Boot.
*/
/**
* The boot method is called early in the boot process before any
* allocation.
*/
@Interruptible
public void boot() {
}
/**
* The postBoot method is called by the runtime immediately after
* command-line arguments are available. Note that allocation must
* be supported prior to this point because the runtime
* infrastructure may require allocation in order to parse the
* command line arguments. For this reason all plans should operate
* gracefully on the default minimum heap size until the point that
* boot is called.
*/
@Interruptible
public void postBoot() {
VM.statistics.perfCtrInit(Options.perfMetric.getValue());
if (Options.verbose.getValue() > 2) Space.printVMMap();
if (Options.verbose.getValue() > 3) VM.config.printConfig();
if (Options.verbose.getValue() > 0) Stats.startAll();
if (Options.eagerMmapSpaces.getValue()) Space.eagerlyMmapMMTkSpaces();
}
/**
* The fullyBooted method is called by the runtime just before normal
* execution commences.
*/
@Interruptible
public void fullyBooted() {
initialized = true;
if (Options.harnessAll.getValue()) harnessBegin();
}
/**
* The VM is about to exit. Perform any clean up operations.
*
* @param value The exit value
*/
@Interruptible
public void notifyExit(int value) {
if (Options.harnessAll.getValue()) harnessEnd();
if (Options.verbose.getValue() == 1) {
Log.write("[End ");
totalTime.printTotalSecs();
Log.writeln(" s]");
} else if (Options.verbose.getValue() == 2) {
Log.write("[End ");
totalTime.printTotalMillis();
Log.writeln(" ms]");
}
if (Options.verboseTiming.getValue()) printDetailedTiming(true);
}
/**
* Any Plan can override this to provide additional plan specific
* timing information.
*
* @param totals Print totals
*/
protected void printDetailedTiming(boolean totals) {}
/**
* 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 (NEEDS_LOG_BIT_IN_HEADER)
return status.or(UNLOGGED_BIT);
else
return status; // nothing to do (no bytes of GC header)
}
/**
* Perform any required write barrier action when installing an object reference
* a boot time.
*
* @param reference the reference value that is to be stored
* @return The raw value to be
*/
public Word bootTimeWriteBarrier(Word reference) {
return reference;
}
/****************************************************************************
* Allocation
*/
public static int getAllocationSite(boolean compileTime) {
if (compileTime) // a new allocation site is being compiled
return allocationSiteCount++;
else // an anonymous site
return DEFAULT_SITE;
}
/****************************************************************************
* Collection.
*/
/**
* Perform a (global) collection phase.
*/
public abstract void collectionPhase(short phase);
/**
* Replace a phase.
*
* @param oldScheduledPhase The scheduled phase to insert after
* @param scheduledPhase The scheduled phase to insert
*/
@Interruptible
public void replacePhase(int oldScheduledPhase, int scheduledPhase) {
VM.assertions.fail("replacePhase not implemented for this plan");
}
/**
* Insert a phase.
*
* @param markerScheduledPhase The scheduled phase to insert after
* @param scheduledPhase The scheduled phase to insert
*/
@Interruptible
public void insertPhaseAfter(int markerScheduledPhase, int scheduledPhase) {
short tempPhase = Phase.createComplex("auto-gen", null, markerScheduledPhase, scheduledPhase);
replacePhase(markerScheduledPhase, Phase.scheduleComplex(tempPhase));
}
/**
* @return Whether last GC was an exhaustive attempt to collect the heap. For many collectors this is the same as asking whether the last GC was a full heap collection.
*/
public boolean lastCollectionWasExhaustive() {
return lastCollectionFullHeap();
}
/**
* @return Whether last GC is a full GC.
*/
public boolean lastCollectionFullHeap() {
return true;
}
/**
* @return Is last GC a full collection?
*/
public static boolean isEmergencyCollection() {
return emergencyCollection;
}
/**
* @return True if we have run out of heap space.
*/
public final boolean lastCollectionFailed() {
return !(collectionTrigger == Collection.EXTERNAL_GC_TRIGGER ||
collectionTrigger == Collection.INTERNAL_PHASE_GC_TRIGGER) &&
(getPagesAvail() < getHeapFullThreshold() || getPagesAvail() < requiredAtStart);
}
/**
* Force the next collection to be full heap.
*/
public void forceFullHeapCollection() {}
/**
* @return Is current GC only collecting objects allocated since last GC.
*/
public boolean isCurrentGCNursery() {
return false;
}
private long lastStressPages = 0;
/**
* Return the expected reference count. For non-reference counting
* collectors this becomes a true/false relationship.
*
* @param object The object to check.
* @param sanityRootRC The number of root references to the object.
* @return The expected (root excluded) reference count.
*/
public int sanityExpectedRC(ObjectReference object, int sanityRootRC) {
Space space = Space.getSpaceForObject(object);
return space.isReachable(object) ? SanityChecker.ALIVE : SanityChecker.DEAD;
}
/**
* Perform a linear scan of all spaces to check for possible leaks.
* This is only called after a full-heap GC.
*
* @param scanner The scanner callback to use.
*/
public void sanityLinearScan(LinearScan scanner) {
}
/**
* @return True is a stress test GC is required
*/
@Inline
public final boolean stressTestGCRequired() {
long pages = Space.cumulativeCommittedPages();
if (initialized &&
((pages ^ lastStressPages) > Options.stressFactor.getPages())) {
lastStressPages = pages;
return true;
} else
return false;
}
/****************************************************************************
* GC State
*/
protected static int requiredAtStart;
protected static int collectionTrigger;
protected static boolean emergencyCollection;
protected static boolean awaitingAsyncCollection;
protected static boolean stacksPrepared;
private static boolean initialized = false;
private static boolean collectionTriggered;
@Entrypoint
private static int gcStatus = NOT_IN_GC; // shared variable
/** @return Is the memory management system initialized? */
public static boolean isInitialized() {
return initialized;
}
/**
* Has collection has triggered?
*/
public static boolean isCollectionTriggered() {
return collectionTriggered;
}
/**
* Set that a collection has been triggered.
*/
public static void setCollectionTriggered() {
collectionTriggered = true;
}
/**
* A collection has fully completed. Clear the triggered flag.
*/
public static void collectionComplete() {
collectionTriggered = false;
}
/**
* Return true if stacks have been prepared in this collection cycle.
*
* @return True if stacks have been prepared in this collection cycle.
*/
public static boolean stacksPrepared() {
return stacksPrepared;
}
/**
* Return true if a collection is in progress.
*
* @return True if a collection is in progress.
*/
public static boolean gcInProgress() {
return gcStatus != NOT_IN_GC;
}
/**
* Return true if a collection is in progress and past the preparatory stage.
*
* @return True if a collection is in progress and past the preparatory stage.
*/
public static boolean gcInProgressProper() {
return gcStatus == GC_PROPER;
}
/**
* Sets the GC status.
*
* @param s The new GC status.
*/
public static void setGCStatus(int s) {
if (gcStatus == NOT_IN_GC) {
/* From NOT_IN_GC to any phase */
stacksPrepared = false;
if (Stats.gatheringStats()) {
Stats.startGC();
VM.activePlan.global().printPreStats();
}
}
VM.memory.isync();
gcStatus = s;
VM.memory.sync();
if (gcStatus == NOT_IN_GC) {
/* From any phase to NOT_IN_GC */
if (Stats.gatheringStats()) {
Stats.endGC();
VM.activePlan.global().printPostStats();
}
}
}
/**
* Print out statistics at the start of a GC
*/
public void printPreStats() {
if ((Options.verbose.getValue() == 1) ||
(Options.verbose.getValue() == 2)) {
Log.write("[GC "); Log.write(Stats.gcCount());
if (Options.verbose.getValue() == 1) {
Log.write(" Start ");
Plan.totalTime.printTotalSecs();
Log.write(" s");
} else {
Log.write(" Start ");
Plan.totalTime.printTotalMillis();
Log.write(" ms");
}
Log.write(" ");
Log.write(Conversions.pagesToKBytes(getPagesUsed()));
Log.write("KB ");
Log.flush();
}
if (Options.verbose.getValue() > 2) {
Log.write("Collection "); Log.write(Stats.gcCount());
Log.write(": ");
printUsedPages();
Log.write(" Before Collection: ");
Space.printUsageMB();
if (Options.verbose.getValue() >= 4) {
Log.write(" ");
Space.printUsagePages();
}
if (Options.verbose.getValue() >= 5) {
Space.printVMMap();
}
}
}
/**
* Print out statistics at the end of a GC
*/
public final void printPostStats() {
if ((Options.verbose.getValue() == 1) ||
(Options.verbose.getValue() == 2)) {
Log.write("-> ");
Log.writeDec(Conversions.pagesToBytes(getPagesUsed()).toWord().rshl(10));
Log.write("KB ");
if (Options.verbose.getValue() == 1) {
totalTime.printLast();
Log.writeln(" ms]");
} else {
Log.write("End ");
totalTime.printTotal();
Log.writeln(" ms]");
}
}
if (Options.verbose.getValue() > 2) {
Log.write(" After Collection: ");
Space.printUsageMB();
if (Options.verbose.getValue() >= 4) {
Log.write(" ");
Space.printUsagePages();
}
if (Options.verbose.getValue() >= 5) {
Space.printVMMap();
}
Log.write(" ");
printUsedPages();
Log.write(" Collection time: ");
totalTime.printLast();
Log.writeln(" ms");
}
}
public final void printUsedPages() {
Log.write("reserved = ");
Log.write(Conversions.pagesToMBytes(getPagesReserved()));
Log.write(" MB (");
Log.write(getPagesReserved());
Log.write(" pgs)");
Log.write(" total = ");
Log.write(Conversions.pagesToMBytes(getTotalPages()));
Log.write(" MB (");
Log.write(getTotalPages());
Log.write(" pgs)");
Log.writeln();
}
/**
* Set the collection trigger.
*/
public static void setCollectionTrigger(int trigger) {
collectionTrigger = trigger;
}
/****************************************************************************
* Harness
*/
protected static boolean insideHarness = false;
/**
* Generic hook to allow benchmarks to be harnessed. A plan may use
* this to perform certain actions prior to the commencement of a
* benchmark, such as a full heap collection, turning on
* instrumentation, etc. By default we do a full heap GC,
* and then start stats collection.
*/
@Interruptible
public static void harnessBegin() {
// Save old values.
boolean oldFullHeap = Options.fullHeapSystemGC.getValue();
boolean oldIgnore = Options.ignoreSystemGC.getValue();
// Set desired values.
Options.fullHeapSystemGC.setValue(true);
Options.ignoreSystemGC.setValue(false);
// Trigger a full heap GC.
System.gc();
// Restore old values.
Options.ignoreSystemGC.setValue(oldIgnore);
Options.fullHeapSystemGC.setValue(oldFullHeap);
// Start statistics
insideHarness = true;
Stats.startAll();
}
/**
* Generic hook to allow benchmarks to be harnessed. A plan may use
* this to perform certain actions after the completion of a
* benchmark, such as a full heap collection, turning off
* instrumentation, etc. By default we stop all statistics objects
* and print their values.
*/
@Interruptible
public static void harnessEnd() {
Stats.stopAll();
insideHarness = false;
}
/****************************************************************************
* VM Accounting
*/
/* Global accounting and static access */
/**
* Return the amount of <i>free memory</i>, in bytes (where free is
* defined as not in use). Note that this may overstate the amount
* of <i>available memory</i>, which must account for unused memory
* that is held in reserve for copying, and therefore unavailable
* for allocation.
*
* @return The amount of <i>free memory</i>, in bytes (where free is
* defined as not in use).
*/
public static Extent freeMemory() {
return totalMemory().minus(usedMemory());
}
/**
* Return the amount of <i>available memory</i>, in bytes. Note
* that this accounts for unused memory that is held in reserve
* for copying, and therefore unavailable for allocation.
*
* @return The amount of <i>available memory</i>, in bytes.
*/
public static Extent availableMemory() {
return totalMemory().minus(reservedMemory());
}
/**
* Return the amount of <i>memory in use</i>, in bytes. Note that
* this excludes unused memory that is held in reserve for copying,
* and therefore unavailable for allocation.
*
* @return The amount of <i>memory in use</i>, in bytes.
*/
public static Extent usedMemory() {
return Conversions.pagesToBytes(VM.activePlan.global().getPagesUsed());
}
/**
* Return the amount of <i>memory in use</i>, in bytes. Note that
* this includes unused memory that is held in reserve for copying,
* and therefore unavailable for allocation.
*
* @return The amount of <i>memory in use</i>, in bytes.
*/
public static Extent reservedMemory() {
return Conversions.pagesToBytes(VM.activePlan.global().getPagesReserved());
}
/**
* Return the total amount of memory managed to the memory
* management system, in bytes.
*
* @return The total amount of memory managed to the memory
* management system, in bytes.
*/
public static Extent totalMemory() {
return HeapGrowthManager.getCurrentHeapSize();
}
/* Instance methods */
/**
* Return the total amount of memory managed to the memory
* management system, in pages.
*
* @return The total amount of memory managed to the memory
* management system, in pages.
*/
public final int getTotalPages() {
return totalMemory().toWord().rshl(LOG_BYTES_IN_PAGE).toInt();
}
/**
* Return the number of pages available for allocation.
*
* @return The number of pages available for allocation.
*/
public int getPagesAvail() {
return getTotalPages() - getPagesReserved();
}
/**
* Return the number of pages reserved for use given the pending
* allocation. Sub-classes must override the getCopyReserve method,
* as the arithmetic here is fixed.
*
* @return The number of pages reserved given the pending
* allocation, including space reserved for copying.
*/
public final int getPagesReserved() {
return getPagesUsed() + getCollectionReserve();
}
/**
* Return the number of pages reserved for collection.
* In most cases this is a copy reserve, all subclasses that
* manage a copying space must add the copying contribution.
*
* @return The number of pages reserved given the pending
* allocation, including space reserved for collection.
*/
public int getCollectionReserve() {
return 0;
}
/**
* Return the number of pages reserved for use given the pending
* allocation.
*
* @return The number of pages reserved given the pending
* allocation, excluding space reserved for copying.
*/
public int getPagesUsed() {
return loSpace.reservedPages() +
immortalSpace.reservedPages() + metaDataSpace.reservedPages() +
nonMovingSpace.reservedPages();
}
/**
* 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 loSpace.requiredPages() +
metaDataSpace.requiredPages() + immortalSpace.requiredPages() +
nonMovingSpace.requiredPages();
}
/**
* The minimum number of pages a GC must have available after a collection
* for us to consider the collection successful.
*/
public int getHeapFullThreshold() {
int threshold = (getTotalPages() * HEAP_FULL_PERCENTAGE) / 100;
if (threshold < HEAP_FULL_MINIMUM) threshold = HEAP_FULL_MINIMUM;
return threshold;
}
/**
* Return the number of metadata pages reserved for use given the pending
* allocation.
*
* @return The number of pages reserved given the pending
* allocation, excluding space reserved for copying.
*/
public int getMetaDataPagesUsed() {
return metaDataSpace.reservedPages();
}
/**
* Return the cycle time at which this GC should complete.
*
* @return The time cap for this GC (i.e. the time by which it
* should complete).
*/
public static long getTimeCap() {
return timeCap;
}
/****************************************************************************
* Internal read/write barriers.
*/
/**
* Store an object reference
*
* @param slot The location of the reference
* @param value The value to store
*/
@Inline
public void storeObjectReference(Address slot, ObjectReference value) {
slot.store(value);
}
/**
* Load an object reference
*
* @param slot The location of the reference
* @return the object reference loaded from slot
*/
@Inline
public ObjectReference loadObjectReference(Address slot) {
return slot.loadObjectReference();
}
/****************************************************************************
* Collection.
*/
/**
* This method is called periodically by the allocation subsystem
* (by default, each time a page is consumed), and provides the
* collector with an opportunity to collect.
*
* @param spaceFull Space request failed, must recover pages within 'space'.
* @param space The space that triggered the poll.
* @return true if a collection is required.
*/
@LogicallyUninterruptible
public final boolean poll(boolean spaceFull, Space space) {
if (isCollectionTriggered()) {
if (space == metaDataSpace) {
/* This is not, in general, in a GC safe point. */
return false;
}
/* Someone else initiated a collection, we should join it */
logPoll(space, "Joining collection");
VM.collection.joinCollection();
return true;
}
if (collectionRequired(spaceFull)) {
if (space == metaDataSpace) {
/* In general we must not trigger a GC on metadata allocation since
* this is not, in general, in a GC safe point. Instead we initiate
* an asynchronous GC, which will occur at the next safe point.
*/
logPoll(space, "Asynchronous collection requested");
setAwaitingAsyncCollection();
return false;
}
logPoll(space, "Triggering collection");
VM.collection.triggerCollection(Collection.RESOURCE_GC_TRIGGER);
return true;
}
if (concurrentCollectionRequired()) {
logPoll(space, "Triggering collection");
VM.collection.triggerCollection(Collection.INTERNAL_PHASE_GC_TRIGGER);
return true;
}
return false;
}
/**
* Check whether an asynchronous collection is pending.<p>
*
* This is decoupled from the poll() mechanism because the
* triggering of asynchronous collections can trigger write
* barriers, which can trigger an asynchronous collection. Thus, if
* the triggering were tightly coupled with the request to alloc()
* within the write buffer code, then inifinite regress could
* result. There is no race condition in the following code since
* there is no harm in triggering the collection more than once,
* thus it is unsynchronized.
*/
@Inline
public static void checkForAsyncCollection() {
if (awaitingAsyncCollection && VM.collection.noThreadsInGC()) {
awaitingAsyncCollection = false;
VM.collection.triggerAsyncCollection(Collection.RESOURCE_GC_TRIGGER);
}
}
/** Request an async GC */
protected static void setAwaitingAsyncCollection() {
awaitingAsyncCollection = true;
}
/**
* Log a message from within 'poll'
* @param space
* @param message
*/
private void logPoll(Space space, String message) {
if (Options.verbose.getValue() >= 3) {
Log.write(" [POLL] ");
Log.write(space.getName());
Log.write(": ");
Log.writeln(message);
}
}
/**
* 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.
*/
protected boolean collectionRequired(boolean spaceFull) {
boolean stressForceGC = stressTestGCRequired();
boolean heapFull = getPagesReserved() > getTotalPages();
return spaceFull || stressForceGC || heapFull;
}
/**
* This method controls the triggering of an atomic phase of a concurrent
* collection. It is called periodically during allocation.
*
* @return True if a collection is requested by the plan.
*/
protected boolean concurrentCollectionRequired() {
return false;
}
/**
* Start GCspy server.
*
* @param port The port to listen on,
* @param wait Should we wait for a client to connect?
*/
@Interruptible
public void startGCspyServer(int port, boolean wait) {
VM.assertions.fail("startGCspyServer called on non GCspy plan");
}
/**
* Can this object ever move. Used by the VM to make decisions about
* whether it needs to copy IO buffers etc.
*
* @param object The object in question
* @return True if it is not possible that the object will ever move.
*/
public boolean willNeverMove(ObjectReference object) {
if (!VM.activePlan.constraints().movesObjects())
return true;
if (Space.isInSpace(LOS, object))
return true;
if (Space.isInSpace(IMMORTAL, object))
return true;
if (Space.isInSpace(VM_SPACE, object))
return true;
if (Space.isInSpace(NON_MOVING, object))
return true;
if (USE_CODE_SPACE && Space.isInSpace(SMALL_CODE, object))
return true;
if (USE_CODE_SPACE && Space.isInSpace(LARGE_CODE, object))
return true;
/*
* Default to false- this preserves correctness over efficiency.
* Individual plans should override for non-moving spaces they define.
*/
return false;
}
/****************************************************************************
* Support for logging bits (this is cross-cutting).
*/
/**
* Return true if the specified object needs to be logged.
*
* @param src The object in question
* @return True if the object in question needs to be logged (remembered).
*/
public static final boolean logRequired(ObjectReference src) {
int value = VM.objectModel.readAvailableByte(src);
return !((value & LOG_SET_MASK.toInt()) == 0);
}
/**
* Mark an object as logged. Since duplicate logging does
* not raise any correctness issues, we do <i>not</i> worry
* about synchronization and allow threads to race to log the
* object, potentially including it twice (unlike reference
* counting where duplicates would lead to incorrect reference
* counts).
*
* @param object The object to be marked as logged
*/
public static final void markAsLogged(ObjectReference object) {
int value = VM.objectModel.readAvailableByte(object);
VM.objectModel.writeAvailableByte(object, (byte) (value & LOG_CLEAR_MASK.toInt()));
}
/**
* Mark an object as unlogged.
*
* @param object The object to be marked as unlogged
*/
public static final void markAsUnlogged(ObjectReference object) {
int value = VM.objectModel.readAvailableByte(object);
VM.objectModel.writeAvailableByte(object, (byte) (value | UNLOGGED_BIT.toInt()));
}
/****************************************************************************
* Specialized Methods
*/
/**
* Register specialized methods.
*/
@Interruptible
protected void registerSpecializedMethods() {
}
/**
* Get the specialized scan with the given id.
*/
public final Class<?> getSpecializedScanClass(int id) {
return TransitiveClosure.getSpecializedScanClass(id);
}
}