/*
* 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.Space;
import org.mmtk.utility.Log;
import org.mmtk.utility.deque.*;
import org.mmtk.utility.options.Options;
import org.mmtk.vm.VM;
import org.vmmagic.pragma.*;
import org.vmmagic.unboxed.*;
/**
* This abstract class and its global counterpart implement the core
* functionality for a transitive closure over the heap graph. This class
* specifically implements the unsynchronized thread-local component
* (ie the 'fast path') of the trace mechanism.
*
* @see org.mmtk.plan.Plan
* @see org.mmtk.plan.Trace
*/
@Uninterruptible
public abstract class TraceLocal extends TransitiveClosure {
/****************************************************************************
*
* Instance variables
*/
/** gray objects */
protected final ObjectReferenceDeque values;
/** delayed root slots */
protected final AddressDeque rootLocations;
/****************************************************************************
*
* Initialization
*/
/**
* Constructor
*
* @param trace The global trace class to use.
*/
public TraceLocal(Trace trace) {
this(-1, trace);
}
/**
* Constructor
*
* @param specializedScan The specialized scan id.
* @param trace The global trace class to use.
*/
public TraceLocal(int specializedScan, Trace trace) {
super(specializedScan);
values = new ObjectReferenceDeque("value", trace.valuePool);
rootLocations = new AddressDeque("roots", trace.rootLocationPool);
}
/****************************************************************************
*
* Internally visible Object processing and tracing
*/
/**
* @return whether reference values should be overwritten as the heap is traced
*/
protected boolean overwriteReferenceDuringTrace() {
return true;
}
/**
* Trace a reference during GC. This involves determining which
* collection policy applies and calling the appropriate
* <code>trace</code> method.
*
* @param source The source of the reference.
* @param slot The location containing the object reference to be
* traced. The object reference is <i>NOT</i> an interior pointer.
*/
@Override
@Inline
public final void processEdge(ObjectReference source, Address slot) {
ObjectReference object = VM.activePlan.global().loadObjectReference(slot);
ObjectReference newObject = traceObject(object, false);
if (overwriteReferenceDuringTrace()) {
VM.activePlan.global().storeObjectReference(slot, newObject);
}
}
/**
* Report a root edge to be processed during GC. As the given reference
* may theoretically point to an object required during root scanning,
* the caller has requested processing be delayed.
*
* NOTE: delayed roots are assumed to be raw.
*
* @param slot The location containing the object reference to be
* traced. The object reference is <i>NOT</i> an interior pointer.
*/
@Inline
public final void reportDelayedRootEdge(Address slot) {
rootLocations.push(slot);
}
/**
* Trace a reference during GC. This involves determining which
* collection policy applies and calling the appropriate
* <code>trace</code> method.
*
* @param slot The location containing the object reference to be
* traced. The object reference is <i>NOT</i> an interior pointer.
* @param untraced <code>true</code> if <code>objLoc</code> is an untraced root.
*/
@Inline
public final void processRootEdge(Address slot, boolean untraced) {
ObjectReference object;
if (untraced) object = slot.loadObjectReference();
else object = VM.activePlan.global().loadObjectReference(slot);
ObjectReference newObject = traceObject(object, true);
if (overwriteReferenceDuringTrace()) {
if (untraced) slot.store(newObject);
else VM.activePlan.global().storeObjectReference(slot, newObject);
}
}
/**
* Trace a reference during GC. This involves determining which
* collection policy applies and calling the appropriate
* <code>trace</code> method.
*
* @param target The object the interior edge points within.
* @param slot The location of the interior edge.
* @param root <code>true</code> if this is a root edge.
*/
public final void processInteriorEdge(ObjectReference target, Address slot, boolean root) {
Address interiorRef = slot.loadAddress();
Offset offset = interiorRef.diff(target.toAddress());
ObjectReference newTarget = traceObject(target, root);
if (VM.VERIFY_ASSERTIONS) {
if (offset.sLT(Offset.zero()) || offset.sGT(Offset.fromIntSignExtend(1 << 24))) {
// There is probably no object this large
Log.writeln("ERROR: Suspiciously large delta to interior pointer");
Log.writeln(" object base = ", target);
Log.writeln(" interior reference = ", interiorRef);
Log.writeln(" delta = ", offset);
VM.assertions._assert(false);
}
}
if (overwriteReferenceDuringTrace()) {
slot.store(newTarget.toAddress().plus(offset));
}
}
/**
* Collectors that move objects <b>must</b> override this method.
* It performs the deferred scanning of objects which are forwarded
* during bootstrap of each copying collection. Because of the
* complexities of the collection bootstrap (such objects are
* generally themselves gc-critical), the forwarding and scanning of
* the objects must be dislocated. It is an error for a non-moving
* collector to call this method.
*
* @param object The forwarded object to be scanned
*/
@Inline
protected void scanObject(ObjectReference object) {
if (specializedScan >= 0) {
VM.scanning.specializedScanObject(specializedScan, this, object);
} else {
VM.scanning.scanObject(this, object);
}
}
/****************************************************************************
*
* Externally visible Object processing and tracing
*/
/**
* Add a gray object
*
* @param object The object to be enqueued
*/
@Override
@Inline
public final void processNode(ObjectReference object) {
values.push(object);
}
/**
* Flush the local buffers of all deques.
*/
public final void flush() {
values.flushLocal();
rootLocations.flushLocal();
}
/**
* Is the specified object live?
*
* @param object The object.
* @return {@code true} if the object is live.
*/
@Inline
public boolean isLive(ObjectReference object) {
Space space = Space.getSpaceForObject(object);
if (space == Plan.loSpace)
return Plan.loSpace.isLive(object);
else if (space == Plan.nonMovingSpace)
return Plan.nonMovingSpace.isLive(object);
else if (Plan.USE_CODE_SPACE && space == Plan.smallCodeSpace)
return Plan.smallCodeSpace.isLive(object);
else if (Plan.USE_CODE_SPACE && space == Plan.largeCodeSpace)
return Plan.largeCodeSpace.isLive(object);
else if (space == null) {
if (VM.VERIFY_ASSERTIONS) {
Log.writeln("space failure: ", object);
}
}
return true;
}
/**
* Is the specified object reachable? Used for GC Trace
*
* @param object The object.
* @return {@code true} if the object is live.
*/
@Inline
public boolean isReachable(ObjectReference object) {
return Space.getSpaceForObject(object).isReachable(object);
}
/**
* Is the specified referent of a reference type object live?
*
* @param object The object.
* @return {@code true} if the reference object is live.
*/
@Inline
public boolean isReferentLive(ObjectReference object) {
return isLive(object);
}
/**
* This method is the core method during the trace of the object graph.
* The role of this method is to:
*
* <ol>
* <li>Ensure the traced object is not collected.</li>
* <li>If this is the first visit to the object enqueue it to be scanned.</li>
* <li>Return the forwarded reference to the object.</li>
* </ol>
*
* @param object The object to be traced.
* @return The new reference to the same object instance.
*/
@Inline
public ObjectReference traceObject(ObjectReference object) {
if (Space.isInSpace(Plan.VM_SPACE, object))
return (Plan.SCAN_BOOT_IMAGE) ? object : Plan.vmSpace.traceObject(this, object);
if (Space.isInSpace(Plan.IMMORTAL, object))
return Plan.immortalSpace.traceObject(this, object);
if (Space.isInSpace(Plan.LOS, object))
return Plan.loSpace.traceObject(this, object);
if (Space.isInSpace(Plan.NON_MOVING, object))
return Plan.nonMovingSpace.traceObject(this, object);
if (Plan.USE_CODE_SPACE && Space.isInSpace(Plan.SMALL_CODE, object))
return Plan.smallCodeSpace.traceObject(this, object);
if (Plan.USE_CODE_SPACE && Space.isInSpace(Plan.LARGE_CODE, object))
return Plan.largeCodeSpace.traceObject(this, object);
if (VM.VERIFY_ASSERTIONS) {
Log.writeln("Failing object => ", object);
Space.printVMMap();
VM.assertions._assert(false, "No special case for space in traceObject");
}
return ObjectReference.nullReference();
}
/**
* This method traces an object with knowledge of the fact that object
* is a root or not. In simple collectors the fact it is a root is not
* important so this is the default implementation given here.
*
* @param object The object to be traced.
* @param root Is this object a root?
* @return The new reference to the same object instance.
*/
@Inline
public ObjectReference traceObject(ObjectReference object, boolean root) {
return traceObject(object);
}
/**
* Ensure that the referenced object will not move from this point through
* to the end of the collection. This can involve forwarding the object
* if necessary.
*
* <i>Non-copying collectors do nothing, copying collectors must
* override this method in each of their trace classes.</i>
*
* @param object The object that must not move during the collection.
* @return {@code true} If the object will not move during collection
*/
public boolean willNotMoveInCurrentCollection(ObjectReference object) {
if (!VM.activePlan.constraints().movesObjects())
return true;
if (Space.isInSpace(Plan.LOS, object))
return true;
if (Space.isInSpace(Plan.IMMORTAL, object))
return true;
if (Space.isInSpace(Plan.VM_SPACE, object))
return true;
if (Space.isInSpace(Plan.NON_MOVING, object))
return true;
if (Plan.USE_CODE_SPACE && Space.isInSpace(Plan.SMALL_CODE, object))
return true;
if (Plan.USE_CODE_SPACE && Space.isInSpace(Plan.LARGE_CODE, object))
return true;
if (VM.VERIFY_ASSERTIONS)
VM.assertions._assert(false, "willNotMove not defined properly in subclass");
return false;
}
/**
* If a Finalizable object has moved, return the new location.
*
* @param object The object which may have been forwarded.
* @return The new location of <code>object</code>.
*/
public ObjectReference getForwardedFinalizable(ObjectReference object) {
return getForwardedReference(object);
}
/**
* If the reference object (from a Reference Type) has object has moved,
* return the new location.
*
* @param object The object which may have been forwarded.
* @return The new location of <code>object</code>.
*/
@Inline
public ObjectReference getForwardedReferent(ObjectReference object) {
return getForwardedReference(object);
}
/**
* If the Reference Type object has moved, return the new location.
*
* @param object The object which may have been forwarded.
* @return The new location of <code>object</code>.
*/
@Inline
public ObjectReference getForwardedReferenceType(ObjectReference object) {
return getForwardedReference(object);
}
/**
* If the referenced object has moved, return the new location.
*
* Some copying collectors will need to override this method.
*
* @param object The object which may have been forwarded.
* @return The new location of <code>object</code>.
*/
@Inline
public ObjectReference getForwardedReference(ObjectReference object) {
return traceObject(object);
}
/**
* Make alive a referent object that is known not to be live
* (isLive is false). This is used by the ReferenceProcessor.
*
* <i>For many collectors these semantics reflect those of
* <code>traceObject</code>, which is implemented here. Other
* collectors must override this method.</i>
*
* @param object The object which is to be made alive.
* @return The possibly forwarded address of the object.
*/
@Inline
public ObjectReference retainReferent(ObjectReference object) {
return traceObject(object);
}
/**
* An object is unreachable and is about to be added to the
* finalizable queue. The collector must ensure the object is not
* collected (despite being otherwise unreachable), and should
* return its forwarded address if keeping the object alive involves
* forwarding. This is only ever called once for an object.<p>
*
* <i>For many collectors these semantics reflect those of
* <code>traceObject</code>, which is implemented here. Other
* collectors must override this method.</i><p>
*
* TODO is the JavaDoc for this method still up to date?
*
* @param object The object which may have been forwarded.
* @return The forwarded value for <code>object</code>. <i>In this
* case return <code>object</code></i>, copying collectors must override
* this method.
*/
public ObjectReference retainForFinalize(ObjectReference object) {
return traceObject(object);
}
/**
* Return true if an object is ready to move to the finalizable
* queue, i.e. it has no regular references to it. This method may
* (and in some cases is) be overridden by subclasses. If this method
* returns true then it can be assumed that retainForFinalize will be
* called during the current collection.
*
* <i>For many collectors these semantics reflect those of
* <code>isLive</code>, which is implemented here. Other
* collectors must override this method.</i>
*
* @param object The object being queried.
* @return <code>true</code> if the object has no regular references
* to it.
*/
public boolean readyToFinalize(ObjectReference object) {
return !isLive(object);
}
/****************************************************************************
*
* Collection
*
* Important notes:
* . Global actions are executed by only one thread
* . Thread-local actions are executed by all threads
* . The following order is guaranteed by BasePlan, with each
* separated by a synchronization barrier.
* 1. globalPrepare()
* 2. threadLocalPrepare()
* 3. threadLocalRelease()
* 4. globalRelease()
*/
/**
* TODO write JavaDoc comment
*/
public void prepare() {
// Nothing to do
}
public void release() {
values.reset();
rootLocations.reset();
}
/**
* Process any roots for which processing was delayed.
*/
@Inline
public void processRoots() {
logMessage(5, "processing delayed root objects");
while (!rootLocations.isEmpty()) {
processRootEdge(rootLocations.pop(), true);
}
}
/**
* Finishing processing all GC work. This method iterates until all work queues
* are empty.
*/
@Inline
public void completeTrace() {
logMessage(4, "Processing GC in parallel");
if (!rootLocations.isEmpty()) {
processRoots();
}
logMessage(5, "processing gray objects");
assertMutatorRemsetsFlushed();
do {
while (!values.isEmpty()) {
ObjectReference v = values.pop();
scanObject(v);
}
processRememberedSets();
} while (!values.isEmpty());
assertMutatorRemsetsFlushed();
}
/**
* Process GC work until either complete or workLimit
* units of work are completed.
*
* @param workLimit The maximum units of work to perform.
* @return <code>true</code> if all work was completed within workLimit.
*/
@Inline
public boolean incrementalTrace(int workLimit) {
logMessage(4, "Continuing GC in parallel (incremental)");
logMessage(5, "processing gray objects");
int units = 0;
do {
while (!values.isEmpty() && units < workLimit) {
ObjectReference v = values.pop();
scanObject(v);
units++;
}
} while (!values.isEmpty() && units < workLimit);
return values.isEmpty();
}
/**
* Flush any remembered sets pertaining to the current collection.
* Non-generational collectors do nothing.
*/
protected void processRememberedSets() {}
/**
* Assert that the remsets have been flushed. This is critical to
* correctness. We need to maintain the invariant that remset entries
* do not accrue during GC. If the host JVM generates barrier entries
* it is its own responsibility to ensure that they are flushed before
* returning to MMTk.
*/
private void assertMutatorRemsetsFlushed() {
/* FIXME: PNT
if (VM.VERIFY_ASSERTIONS) {
for (int m = 0; m < VM.activePlan.mutatorCount(); m++) {
VM.activePlan.mutator(m).assertRemsetsFlushed();
}
}
*/
}
/**
* This method logs a message with prepended thread id, if the
* verbosity level is greater or equal to the passed level.
*
* @param minVerbose The required verbosity level
* @param message The message to display
*/
@Inline
protected final void logMessage(int minVerbose, String message) {
if (Options.verbose.getValue() >= minVerbose) {
Log.prependThreadId();
Log.write(" ");
Log.writeln(message);
}
}
}