/*
* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.vm.heap.sequential.semiSpace;
import static com.sun.max.vm.VMOptions.*;
import static com.sun.max.vm.heap.Heap.*;
import static com.sun.max.vm.intrinsics.MaxineIntrinsicIDs.*;
import static com.sun.max.vm.thread.VmThread.*;
import static com.sun.max.vm.thread.VmThreadLocal.*;
import java.lang.management.*;
import com.sun.management.GarbageCollectorMXBean;
import com.sun.max.annotate.*;
import com.sun.max.memory.*;
import com.sun.max.platform.*;
import com.sun.max.unsafe.*;
import com.sun.max.util.timer.*;
import com.sun.max.vm.*;
import com.sun.max.vm.actor.holder.*;
import com.sun.max.vm.code.*;
import com.sun.max.vm.heap.*;
import com.sun.max.vm.heap.Heap.GCCallbackPhase;
import com.sun.max.vm.heap.debug.*;
import com.sun.max.vm.layout.*;
import com.sun.max.vm.log.VMLog.Record;
import com.sun.max.vm.log.*;
import com.sun.max.vm.log.VMLogger.Interval;
import com.sun.max.vm.log.hosted.*;
import com.sun.max.vm.management.*;
import com.sun.max.vm.reference.*;
import com.sun.max.vm.runtime.*;
import com.sun.max.vm.thread.*;
import com.sun.max.vm.ti.*;
/**
* A simple semispace scavenger heap.
*/
public class SemiSpaceHeapScheme extends HeapSchemeWithTLAB implements CellVisitor {
public static final String FROM_REGION_NAME = "Heap-SemiSpace-From";
public static final String TO_REGION_NAME = "Heap-SemiSpace-To";
public static final String FROM_GROW_REGION_NAME = "Heap-SemiSpace-From-Grow";
public static final String TO_GROW_REGION_NAME = "Heap-SemiSpace-To-Grow";
public static final String LINEAR_GROW_POLICY_NAME = "Linear";
public static final String DOUBLE_GROW_POLICY_NAME = "Double";
public static final String NO_GROW_POLICY_NAME = "None";
/**
* A VM option for specifying amount of memory to be reserved for allocating and raising an
* OutOfMemoryError when insufficient memory is available to satisfy an allocation request.
*
* @see #safetyZoneSize
*/
private static final VMIntOption safetyZoneSizeOption =
register(new VMIntOption("-XX:OutOfMemoryZoneSize=", 6144,
"Memory reserved to throw OutOfMemoryError. If using TLABs, then the actual memory reserved " +
"is the maximum of this option's value and the size of a TLAB."), MaxineVM.Phase.PRISTINE);
private static final VMStringOption growPolicyOption =
register(new VMStringOption("-XX:HeapGrowPolicy=", false, DOUBLE_GROW_POLICY_NAME, "Grow policy for heap (Linear|Double)."), MaxineVM.Phase.STARTING);
/**
* Procedure used to update a reference so that it points to an object in 'toSpace'.
*/
private final RefUpdater refUpdater = new RefUpdater();
private final GC refForwarder = new GC();
/**
* The procedure that will identify all the GC roots except those in the boot heap and code regions.
*/
private final SequentialHeapRootsScanner heapRootsScanner = new SequentialHeapRootsScanner(refUpdater);
/**
* A VM option for enabling extra checking of references. This should be disabled when running GC benchmarks.
* It's enabled by default as the primary goal of this collector are simplicity and robustness,
* not high performance.
*/
private static boolean VerifyReferences = true;
static {
VMOptions.addFieldOption("-XX:", "VerifyReferences", SemiSpaceHeapScheme.class, "Do extra verification for each reference scanned by the GC", MaxineVM.Phase.PRISTINE);
}
private final CollectHeap collectHeap;
@INSPECTED
private LinearAllocationMemoryRegion fromSpace = new LinearAllocationMemoryRegion(FROM_REGION_NAME);
@INSPECTED
private LinearAllocationMemoryRegion toSpace = new LinearAllocationMemoryRegion(TO_REGION_NAME);
/**
* Used when {@linkplain #grow(GrowPolicy) growing} the heap.
*/
private final LinearAllocationMemoryRegion growFromSpace = new LinearAllocationMemoryRegion(FROM_GROW_REGION_NAME);
/**
* Used when {@linkplain #grow(GrowPolicy) growing} the heap.
*/
private final LinearAllocationMemoryRegion growToSpace = new LinearAllocationMemoryRegion(TO_GROW_REGION_NAME);
/**
* The amount of memory reserved for allocating and raising an OutOfMemoryError when insufficient
* memory is available to satisfy an allocation request.
*
* @see #safetyZoneSizeOption
*/
private int safetyZoneSize;
private GrowPolicy growPolicy;
private LinearGrowPolicy increaseGrowPolicy;
/**
* The global allocation limit (minus the {@linkplain #safetyZoneSize safety zone}).
*/
private Address top;
private final ResetTLAB resetTLAB = new ResetTLAB(){
@Override
protected void doBeforeReset(Pointer etla, Pointer tlabMark, Pointer tlabEnd) {
if (MaxineVM.isDebug()) {
padTLAB(etla, tlabMark, tlabEnd);
}
}
};
@Override
protected void tlabReset(Pointer tla) {
resetTLAB.run(tla);
}
// Create timing facilities.
private final TimerMetric clearTimer = new TimerMetric(new SingleUseTimer(HeapScheme.GC_TIMING_CLOCK));
private final TimerMetric gcTimer = new TimerMetric(new SingleUseTimer(HeapScheme.GC_TIMING_CLOCK));
private final TimerMetric rootScanTimer = new TimerMetric(new SingleUseTimer(HeapScheme.GC_TIMING_CLOCK));
private final TimerMetric bootHeapScanTimer = new TimerMetric(new SingleUseTimer(HeapScheme.GC_TIMING_CLOCK));
private final TimerMetric codeScanTimer = new TimerMetric(new SingleUseTimer(HeapScheme.GC_TIMING_CLOCK));
private final TimerMetric immortalSpaceScanTimer = new TimerMetric(new SingleUseTimer(HeapScheme.GC_TIMING_CLOCK));
private final TimerMetric copyTimer = new TimerMetric(new SingleUseTimer(HeapScheme.GC_TIMING_CLOCK));
private final TimerMetric weakRefTimer = new TimerMetric(new SingleUseTimer(HeapScheme.GC_TIMING_CLOCK));
private long lastGCTime;
/**
* Procedure used to verify a reference.
*/
private final DebugHeap.RefVerifier refVerifier = new DebugHeap.RefVerifier(toSpace);
/**
* Procedure used to verify GC root reference well-formedness.
*/
private final SequentialHeapRootsScanner gcRootsVerifier = new SequentialHeapRootsScanner(refVerifier);
/**
* A VM option for triggering a GC before every allocation.
*/
static boolean GCBeforeAllocation;
static {
VMOptions.addFieldOption("-XX:", "GCBeforeAllocation", SemiSpaceHeapScheme.class,
"Perform a garbage collection before every allocation from the global heap.", MaxineVM.Phase.PRISTINE);
}
public SemiSpaceHeapScheme() {
super();
pinningSupportFlags = PIN_SUPPORT_FLAG.makePinSupportFlags(false, false, false);
collectHeap = new CollectHeap();
}
@Override
public void initialize(MaxineVM.Phase phase) {
super.initialize(phase);
if (phase == MaxineVM.Phase.PRISTINE) {
allocateHeap();
safetyZoneSize = Math.max(safetyZoneSizeOption.getValue(), initialTlabSize().toInt());
top = toSpace.end().minus(safetyZoneSize);
if (MaxineVM.isDebug()) {
zapRegion(toSpace, GCCallbackPhase.INIT);
}
if (MaxineVM.isDebug()) {
VerifyReferences = true;
}
lastGCTime = System.currentTimeMillis();
// From now on we can allocate
HeapScheme.Inspect.init(true);
HeapScheme.Inspect.notifyHeapRegions(toSpace, fromSpace);
Heap.invokeGCCallbacks(GCCallbackPhase.INIT);
} else if (phase == MaxineVM.Phase.STARTING) {
final String growPolicy = growPolicyOption.getValue();
if (growPolicy.equals(DOUBLE_GROW_POLICY_NAME)) {
this.growPolicy = new DoubleGrowPolicy();
} else if (growPolicy.equals(LINEAR_GROW_POLICY_NAME)) {
this.growPolicy = new LinearGrowPolicy(growPolicy);
} else if (growPolicy.equals(NO_GROW_POLICY_NAME)) {
this.growPolicy = null;
} else {
Log.print("Unknown heap growth policy, using default policy");
this.growPolicy = new DoubleGrowPolicy();
}
increaseGrowPolicy = new LinearGrowPolicy();
} else if (phase == MaxineVM.Phase.TERMINATING) {
if (Heap.logGCTime()) {
timeLogger.logPhaseTimes(-1,
clearTimer.getElapsedTime(), rootScanTimer.getElapsedTime(), bootHeapScanTimer.getElapsedTime(),
codeScanTimer.getElapsedTime(), copyTimer.getElapsedTime(),
weakRefTimer.getElapsedTime(), gcTimer.getElapsedTime());
}
}
}
private void allocateHeap() {
boolean heapAllocationOk;
final Size size = Heap.initialSize().dividedBy(2);
Size heapAllocationSize = size;
if (!Heap.gcDisabled()) {
heapAllocationOk = !allocateSpace(fromSpace, size).isZero() && !allocateSpace(toSpace, size).isZero();
} else {
// If GC is disabled, then use all of -Xmx for toSpace
heapAllocationSize = Heap.maxSize();
heapAllocationOk = !allocateSpace(toSpace, heapAllocationSize).isZero();
}
if (!heapAllocationOk) {
MaxineVM.reportPristineMemoryFailure("object heap", "allocate", heapAllocationSize);
} else {
if (Heap.verbose()) {
Log.print("Allocated ");
Log.print(heapAllocationSize.toLong());
Log.println(" bytes of memory for object heap");
}
}
}
@INLINE
private Address allocationMark() {
return toSpace.mark().asAddress();
}
private static void startTimer(Timer timer) {
if (Heap.logGCTime()) {
timer.start();
}
}
private static void stopTimer(Timer timer) {
if (Heap.logGCTime()) {
timer.stop();
}
}
private final class GC implements SpecialReferenceManager.GC {
public boolean isReachable(Reference ref) {
final Pointer origin = ref.toOrigin();
if (fromSpace.contains(origin)) {
final Reference forwardRef = Layout.readForwardRef(origin);
if (forwardRef.isZero()) {
return false;
}
}
return true;
}
public Reference preserve(Reference ref) {
Pointer oldAllocationMark = allocationMark().asPointer();
Reference newRef = mapRef(ref);
if (!oldAllocationMark.equals(allocationMark().asPointer())) {
moveReachableObjects(oldAllocationMark);
}
return newRef;
}
public boolean mayRelocateLiveObjects() {
return true;
}
}
/**
* A procedure to update a reference so that it points to an object in 'toSpace'.
*
* @see SemiSpaceHeapScheme#mapRef(Reference)
*/
private final class RefUpdater extends PointerIndexVisitor {
@Override
public void visit(Pointer pointer, int wordIndex) {
final Reference oldRef = pointer.getReference(wordIndex);
final Reference newRef = mapRef(oldRef);
if (newRef != oldRef) {
pointer.setReference(wordIndex, newRef);
}
}
}
/**
* Routine that performs the actual garbage collection.
*/
final class CollectHeap extends GCOperation {
SemiSpaceGCRequest gcRequest() {
return asSemiSpaceGCRequest(callingThread().gcRequest);
}
public CollectHeap() {
super("CollectHeap");
}
@Override
public void collect(int invocationCount) {
try {
VmThreadMap.ACTIVE.forAllThreadLocals(null, resetTLAB);
Heap.invokeGCCallbacks(GCCallbackPhase.BEFORE);
// Pre-verification of the heap.
verifyObjectSpaces(GCCallbackPhase.BEFORE);
final long startGCTime = System.currentTimeMillis();
collectionCount++;
startTimer(gcTimer);
startTimer(clearTimer);
swapSemiSpaces(); // Swap semispaces. From--> To and To-->From
stopTimer(clearTimer);
refVerifier.setValidSpaces(fromSpace, toSpace);
if (Heap.logGCPhases()) {
phaseLogger.logScanningRoots(VMLogger.Interval.BEGIN);
}
startTimer(rootScanTimer);
heapRootsScanner.run(); // Start scanning the reachable objects from my roots.
stopTimer(rootScanTimer);
if (Heap.logGCPhases()) {
phaseLogger.logScanningRoots(VMLogger.Interval.END);
}
if (Heap.logGCPhases()) {
phaseLogger.logScanningBootHeap(VMLogger.Interval.BEGIN);
}
startTimer(bootHeapScanTimer);
scanBootHeap();
stopTimer(bootHeapScanTimer);
if (Heap.logGCPhases()) {
phaseLogger.logScanningBootHeap(VMLogger.Interval.END);
}
if (Heap.logGCPhases()) {
phaseLogger.logScanningCode(VMLogger.Interval.BEGIN);
}
startTimer(codeScanTimer);
scanCode();
stopTimer(codeScanTimer);
if (Heap.logGCPhases()) {
phaseLogger.logScanningCode(VMLogger.Interval.END);
}
if (Heap.logGCPhases()) {
phaseLogger.logScanningImmortalHeap(VMLogger.Interval.BEGIN);
}
startTimer(immortalSpaceScanTimer);
scanImmortalHeap();
stopTimer(immortalSpaceScanTimer);
if (Heap.logGCPhases()) {
phaseLogger.logScanningImmortalHeap(VMLogger.Interval.END);
}
if (Heap.logGCPhases()) {
phaseLogger.logMovingReachable(VMLogger.Interval.BEGIN);
}
startTimer(copyTimer);
moveReachableObjects(toSpace.start().asPointer());
stopTimer(copyTimer);
if (Heap.logGCPhases()) {
phaseLogger.logMovingReachable(VMLogger.Interval.END);
}
if (Heap.logGCPhases()) {
phaseLogger.logProcessingSpecialReferences(VMLogger.Interval.BEGIN);
}
startTimer(weakRefTimer);
SpecialReferenceManager.processDiscoveredSpecialReferences(refForwarder);
stopTimer(weakRefTimer);
stopTimer(gcTimer);
if (Heap.logGCPhases()) {
phaseLogger.logProcessingSpecialReferences(VMLogger.Interval.END);
}
// Bring the To-Space marks up to date, mainly for debugging.
toSpace.mark.set(allocationMark()); // not otherwise updated during move.
// The reclaiming phase doesn't do anything substantial in a semispace collector since all
// space of the from space is implicitly reclaimed once the liveness analysis (i.e.,
// the copying of all objects reachable from roots) is done.
HeapScheme.Inspect.notifyHeapPhaseChange(HeapPhase.RECLAIMING);
// Now officially mark From-space as having no allocations.
fromSpace.mark.set(fromSpace.start());
final SemiSpaceGCRequest gcRequest = gcRequest();
gcRequest.lastInvocationCount = invocationCount;
lastGCTime = System.currentTimeMillis();
accumulatedGCTime += lastGCTime - startGCTime;
HeapScheme.Inspect.notifyHeapPhaseChange(HeapPhase.MUTATING);
Heap.invokeGCCallbacks(GCCallbackPhase.AFTER);
// Post-verification of the heap.
refVerifier.setValidHeapSpace(toSpace);
verifyObjectSpaces(GCCallbackPhase.AFTER);
if (Heap.logGCTime()) {
timeLogger.logPhaseTimes(invocationCount,
clearTimer.getLastElapsedTime(),
rootScanTimer.getLastElapsedTime(),
bootHeapScanTimer.getLastElapsedTime(),
codeScanTimer.getLastElapsedTime(),
copyTimer.getLastElapsedTime(),
weakRefTimer.getLastElapsedTime(),
gcTimer.getLastElapsedTime());
}
} catch (Throwable throwable) {
FatalError.unexpected("Exception during GC", throwable);
}
}
}
/**
* Attempts to allocate memory of given size for given space.
* If successful sets region start and size.
*/
private static Address allocateSpace(LinearAllocationMemoryRegion space, Size size) {
final Address base = VirtualMemory.allocate(size, VirtualMemory.Type.HEAP);
if (!base.isZero()) {
space.setStart(base);
space.mark.set(base); // debugging
space.setSize(size);
}
return base;
}
/**
* Deallocates the memory associated with the given region.
* Sets the region start to zero but does not change the size.
*/
private static void deallocateSpace(MemoryRegion space) {
VirtualMemory.deallocate(space.start(), space.size(), VirtualMemory.Type.HEAP);
space.setStart(Address.zero());
}
/**
* Copies the state of one space into another.
* Used when growing the semispaces.
*/
private static void copySpaceState(LinearAllocationMemoryRegion from, LinearAllocationMemoryRegion to) {
to.setStart(from.start());
to.mark.set(from.start());
to.setSize(from.size());
}
private void swapSemiSpaces() {
final Address oldFromSpaceStart = fromSpace.start();
final Size oldFromSpaceSize = fromSpace.size();
// Inspectability: stopping the VM between here and the phase notification below will confuse the Inspector
fromSpace.setStart(toSpace.start());
fromSpace.setSize(toSpace.size());
fromSpace.mark.set(toSpace.getAllocationMark());
toSpace.setStart(oldFromSpaceStart);
toSpace.setSize(oldFromSpaceSize);
toSpace.mark.set(toSpace.start());
// For the purposes of inspection, we declare this phase change immediately after the swap;
// The Inspector gets confused it the VM stops after the swap, but when the phase
// appears to still be mutating.
HeapScheme.Inspect.notifyHeapPhaseChange(HeapPhase.ANALYZING);
top = toSpace.end();
// If we are currently using the safety zone, we must not install it in the swapped space
// as that could cause gcAllocate to fail trying to copying too much live data.
if (!inSafetyZone) {
top = top.minus(safetyZoneSize);
}
}
private Size immediateFreeSpace() {
return top.minus(allocationMark()).asSize();
}
/**
* Maps a given reference to the reference of an object in 'toSpace'.
* The action taken depends on which of the three following states {@code ref} is in:
* <ul>
* <li>Points to a not-yet-copied object in 'fromSpace'. The object is
* copied and a forwarding pointer is installed in the header of
* the source object (i.e. the one in 'fromSpace'). The reference of the
* destination object (i.e the one in 'toSpace') is returned.</li>
* <li>Points to a object in 'fromSpace' for which a copy in 'toSpace' exists.
* The reference of the 'toSpace' copy is derived from the forwarding pointer and returned.</li>
* <li>Points to a object in 'toSpace'. The value of {@code ref} is returned.</li>
* </ul>
*
* @param ref a pointer to an object either in 'fromSpace' or 'toSpace'
* @return the reference to the object in 'toSpace' obtained by the algorithm described above
*/
private Reference mapRef(Reference ref) {
final Pointer fromOrigin = ref.toOrigin();
if (fromSpace.contains(fromOrigin)) {
final Reference forwardRef = Layout.readForwardRef(fromOrigin);
if (!forwardRef.isZero()) {
return forwardRef;
}
if (VerifyReferences) {
refVerifier.verifyRefAtIndex(Address.zero(), 0, ref);
}
final Pointer fromCell = Layout.originToCell(fromOrigin);
final Size size = Layout.size(fromOrigin);
final Pointer toCell = gcAllocate(size);
if (DebugHeap.isTagging()) {
DebugHeap.writeCellTag(toCell);
}
if (detailLogger.enabled()) {
final Hub hub = UnsafeCast.asHub(Layout.readHubReference(ref).toJava());
detailLogger.logForward(hub.classActor.id, Pointer.zero(), fromCell, toCell, size.toInt());
}
Memory.copyBytes(fromCell, toCell, size);
final Pointer toOrigin = Layout.cellToOrigin(toCell);
final Reference toRef = Reference.fromOrigin(toOrigin);
Layout.writeForwardRef(fromOrigin, toRef);
return toRef;
}
return ref;
}
private void scanReferenceArray(Pointer origin) {
final int length = Layout.readArrayLength(origin);
for (int index = 0; index < length; index++) {
final Reference oldRef = Layout.getReference(origin, index);
final Reference newRef = mapRef(oldRef);
if (newRef != oldRef) {
Layout.setReference(origin, index, newRef);
}
}
}
/**
* Visits a cell for an object that has been copied to 'toSpace' to
* update any references in the object. If any of the references being
* updated still point to objects in 'fromSpace', then those objects
* will be copied as a side effect of the call to {@link #mapRef(Reference)}
* that yields the updated value of a reference.
*
* @param cell a cell in 'toSpace' to whose references are to be updated
*/
public Pointer visitCell(Pointer cell) {
if (detailLogger.enabled()) {
detailLogger.logVisitCell(cell);
}
final Pointer origin = Layout.cellToOrigin(cell);
// Update the hub first so that is can be dereferenced to obtain
// the reference map needed to find the other references in the object
final Reference oldHubRef = Layout.readHubReference(origin);
final Reference newHubRef = mapRef(oldHubRef);
if (newHubRef != oldHubRef) {
// The hub was copied
Layout.writeHubReference(origin, newHubRef);
}
final Hub hub = UnsafeCast.asHub(newHubRef.toJava());
// Update the other references in the object
final SpecificLayout specificLayout = hub.specificLayout;
if (specificLayout == Layout.tupleLayout()) {
TupleReferenceMap.visitReferences(hub, origin, refUpdater);
if (hub.isJLRReference) {
SpecialReferenceManager.discoverSpecialReference(origin);
}
return cell.plus(hub.tupleSize);
}
if (specificLayout == Layout.referenceArrayLayout()) {
scanReferenceArray(origin);
} else if (specificLayout == Layout.hybridLayout()) {
TupleReferenceMap.visitReferences(hub, origin, refUpdater);
}
return cell.plus(Layout.size(origin));
}
void moveReachableObjects(Pointer start) {
Pointer cell = start;
while (cell.lessThan(allocationMark())) {
cell = DebugHeap.checkDebugCellTag(start, cell);
cell = visitCell(cell);
}
}
/**
* Visit cells corresponding to objects in the heap.
* TLABs complicate this as they are allocated on the
* heap but are not usually filled.
* @param visitor
*/
private void visitCells(CellVisitor visitor) {
Pointer start = toSpace.start().asPointer();
Pointer cell = start;
while (cell.isNotZero() && cell.lessThan(allocationMark()) && cell.getWord().isNotZero()) {
cell = DebugHeap.checkDebugCellTag(start, cell);
cell = visitor.visitCell(cell);
}
}
void scanBootHeap() {
Heap.bootHeapRegion.visitReferences(refUpdater);
}
void scanCode() {
// References in the boot code region are immutable and only ever refer
// to objects in the boot heap region.
boolean includeBootCode = false;
Code.visitCells(this, includeBootCode);
}
void scanImmortalHeap() {
ImmortalHeap.visitCells(this);
}
private boolean cannotGrow() {
return fromSpace.size().isZero() || fromSpace.size().greaterEqual(Heap.maxSize().dividedBy(2));
}
/**
* Grow the semispaces to be of larger size.
*
* @param preGc true if prior to executing collector thread to copy {@link #toSpace} to (grown) {@link #fromSpace}
* @return true iff both spaces can be grown
*/
private boolean growSpaces(boolean preGc, GrowPolicy growPolicy) {
if (preGc && Heap.verbose()) {
Log.println("Trying to grow the heap...");
}
if (cannotGrow()) {
if (preGc && Heap.verbose()) {
Log.println("...failed, max heap size reached");
}
return false;
}
if (preGc) {
// It is important to know now that we can allocate both spaces of the new size
// and, if we cannot, to leave things as they are, so that the VM can continue
// using the safety zone and perhaps then free enough space to continue.
final Size size = Size.min(growPolicy.growth(fromSpace.size()), Heap.maxSize().dividedBy(2));
if (preGc && Heap.verbose()) {
Log.print("...new heap size: ");
Log.println(size.toLong());
}
final Address fromBase = allocateSpace(growFromSpace, size);
final Address tempBase = allocateSpace(growToSpace, size);
if (fromBase.isZero() || tempBase.isZero()) {
if (!fromBase.isZero()) {
deallocateSpace(growFromSpace);
}
if (Heap.verbose()) {
Log.println("...grow failed, can't allocate spaces");
}
return false;
}
// return memory in 'fromSpace'
deallocateSpace(fromSpace);
copySpaceState(growFromSpace, fromSpace);
} else {
// executing the collector thread swapped the spaces
// so we are again updating _fromSpace but with _growToSpace.
deallocateSpace(fromSpace);
copySpaceState(growToSpace, fromSpace);
}
if (preGc && Heap.verbose()) {
Log.println("...grow ok");
}
return true;
}
private void executeGC() {
if (!Heap.gcDisabled()) {
collectHeap.submit();
}
}
private boolean grow(GrowPolicy growPolicy) {
if (growPolicy == null || cannotGrow()) {
return false;
}
boolean result = true;
if (!growSpaces(true, growPolicy)) {
result = false;
} else {
executeGC();
result = growSpaces(false, growPolicy);
}
if (Heap.verbose()) {
logSpaces();
}
return result;
}
private static final class SemiSpaceGCRequest extends GCRequest {
protected SemiSpaceGCRequest(VmThread thread) {
super(thread);
}
}
@INTRINSIC(UNSAFE_CAST)
private static native SemiSpaceGCRequest asSemiSpaceGCRequest(GCRequest gcRequest);
public GCRequest createThreadLocalGCRequest(VmThread vmThread) {
return new SemiSpaceGCRequest(vmThread);
}
public boolean collectGarbage() {
// We invoke the VMTI callbacks here and not via the GCCallBack so that
// they occur on the actual thread causing the GC and not the VM operation thread.
VMTI.handler().beginGC();
boolean result = collectGarbageImpl(asSemiSpaceGCRequest(VmThread.current().gcRequest));
VMTI.handler().endGC();
return result;
}
private boolean collectGarbageImpl(SemiSpaceGCRequest gcRequest) {
final Size requestedFreeSpace = gcRequest.requestedBytes;
if ((gcRequest.explicit && !DisableExplicitGC) || immediateFreeSpace().lessThan(requestedFreeSpace)) {
executeGC();
}
if (immediateFreeSpace().greaterEqual(requestedFreeSpace)) {
// check to see if we can reset safety zone
if (inSafetyZone) {
if (top.minus(allocationMark()).greaterThan(safetyZoneSize)) {
top = top.minus(safetyZoneSize);
inSafetyZone = false;
}
}
return true;
}
while (grow(growPolicy)) {
if (immediateFreeSpace().greaterEqual(requestedFreeSpace)) {
return true;
}
}
return false;
}
public Size reportFreeSpace() {
return immediateFreeSpace();
}
public Size reportUsedSpace() {
return allocationMark().minus(toSpace.start()).asSize();
}
/**
* Allocates space for a cell being copied to 'to space' during GC.
* Note that this allocation is only ever performed by the GC thread and so there's no
* need to use compare-and-swap when updating the allocation mark.
*
* @param size the size of the cell being copied
* @return the start of the allocated cell in 'to space'
*/
public Pointer gcAllocate(Size size) {
Pointer cell = allocationMark().asPointer();
if (DebugHeap.isTagging()) {
cell = cell.plusWords(1);
}
toSpace.mark.set(cell.plus(size));
FatalError.check(allocationMark().lessThan(top), "GC allocation overflow");
return cell;
}
private boolean inSafetyZone; // set after we have thrown OutOfMemoryError and are using the safety zone
@NO_SAFEPOINT_POLLS("heap up to allocation mark must be verifiable if debug tagging")
@Override
protected void doBeforeTLABRefill(Pointer tlabAllocationMark, Pointer tlabEnd) {
if (MaxineVM.isDebug()) {
final Pointer etla = ETLA.load(currentTLA());
padTLAB(etla, tlabAllocationMark, tlabEnd);
}
}
/**
* Allocate a chunk of memory of the specified size and refill a thread's TLAB with it.
* @param etla the thread whose TLAB will be refilled
* @param tlabSize the size of the chunk of memory used to refill the TLAB
*/
@NO_SAFEPOINT_POLLS("heap up to allocation mark must be verifiable if debug tagging")
private void allocateAndRefillTLAB(Pointer etla, Size tlabSize) {
Pointer tlab = retryAllocate(tlabSize, false);
refillTLAB(etla, tlab, tlabSize);
}
@Override
protected Pointer customAllocate(Pointer customAllocator, Size size) {
// Default is to use the immortal heap.
return ImmortalHeap.allocate(size, true);
}
/**
* Handling of TLAB Overflow. This may refill the TLAB or allocate memory directly from the underlying heap.
* This will always be taken when not using TLABs which is fine as the cost of the
* compare-and-swap on {@link #allocationMark} will dominate.
*
* @param size the allocation size requested to the tlab
* @param etla
* @param tlabMark allocation mark of the tlab
* @param tlabEnd soft limit in the tlab to trigger overflow (may equals the actual end of the TLAB, depending on implementation).
* @throws OutOfMemoryError if the allocation request cannot be satisfied.
* @return the address of the allocated cell. Space for the {@linkplain DebugHeap#writeCellTag(Pointer) debug tag}
* will have been reserved immediately before the allocated cell.
*
*/
@Override
@NEVER_INLINE
@NO_SAFEPOINT_POLLS("heap up to allocation mark must be verifiable if debug tagging")
protected Pointer handleTLABOverflow(Size size, Pointer etla, Pointer tlabMark, Pointer tlabEnd) {
// Should we refill the TLAB ?
final TLABRefillPolicy refillPolicy = TLABRefillPolicy.getForCurrentThread(etla);
if (refillPolicy == null) {
// No policy yet for the current thread. This must be the first time this thread uses a TLAB (it does not have one yet).
FatalError.check(tlabMark.isZero(), "thread must not have a TLAB yet");
if (!usesTLAB()) {
// We're not using TLAB. So let's assign the never refill tlab policy.
TLABRefillPolicy.setForCurrentThread(etla, NEVER_REFILL_TLAB);
return retryAllocate(size, true);
}
// Allocate an initial TLAB and a refill policy. For simplicity, this one is allocated from the TLAB (see comment below).
final Size tlabSize = initialTlabSize();
allocateAndRefillTLAB(etla, tlabSize);
// Let's do a bit of meta-circularity. The TLAB is refilled, and no-one except the current thread can use it.
// So the TLAB allocation is going to succeed here
TLABRefillPolicy.setForCurrentThread(etla, new SimpleTLABRefillPolicy(tlabSize));
// Now, address the initial request. Note that we may recurse down to handleTLABOverflow again here if the
// request is larger than the TLAB size. However, this second call will succeed and allocate outside of the TLAB.
return tlabAllocate(size);
}
final Size nextTLABSize = refillPolicy.nextTlabSize();
if (size.greaterThan(nextTLABSize)) {
// This couldn't be allocated in a TLAB, so go directly to direct allocation routine.
// NOTE: this is where we always go if we don't use TLABs (the "never refill" TLAB policy
// always return zero for the next TLAB size.
return retryAllocate(size, true);
}
if (!refillPolicy.shouldRefill(size, tlabMark)) {
// Size would fit in a new tlab, but the policy says we shouldn't refill the TLAB yet, so allocate directly in the heap.
return retryAllocate(size, true);
}
// Refill TLAB and allocate (we know the request can be satisfied with a fresh TLAB and will therefore succeed).
allocateAndRefillTLAB(etla, nextTLABSize);
return tlabAllocate(size);
}
/**
* Allocates a cell from the global heap, using compare-and-swap to resolve any thread race condition.
*
* If allocation fails in this routine, then a garbage collection is performed. If a collection
* does not free up enough space to satisfy the allocation request, then the heap is expanded.
* If there is still not enough space after heap expansion, a {@link OutOfMemoryError} is thrown.
*
* @param size the requested cell size to be allocated
* @param adjustForDebugTag specifies if an extra word is to be reserved before the cell for the debug tag word
* @return the allocated and zeroed chunk
*/
@NEVER_INLINE
@NO_SAFEPOINT_POLLS("heap up to allocation mark must be verifiable if debug tagging")
private Pointer retryAllocate(Size size, boolean adjustForDebugTag) {
Pointer oldAllocationMark;
Pointer cell;
Address end;
do {
if (GCBeforeAllocation && !VmThread.isAttaching()) {
GCRequest.setGCRequest(size);
Heap.collectGarbage();
}
oldAllocationMark = allocationMark().asPointer();
cell = adjustForDebugTag ? DebugHeap.adjustForDebugTag(oldAllocationMark) : oldAllocationMark;
end = cell.plus(size);
while (end.greaterThan(top)) {
GCRequest.setGCRequest(size);
if (!Heap.collectGarbage()) {
/*
* The OutOfMemoryError condition happens when we cannot satisfy a request after running a garbage collection and we
* cannot grow the heap any further (i.e. we are at the limit set by -Xmx). In that case we raise 'top' to the actual end of
* the active space and set 'inSafetyZone' to true. If this happens recursively we fast fail the VM via MaxineVM.native_exit.
* On the other hand if a subsequent GC manages to find enough space to allow the safety zone to be re-established
* we set 'inSafetyZone' to false.
*/
if (inSafetyZone) {
FatalError.unexpected("Out of memory again after throwing OutOfMemoryError");
} else {
// Use the safety region to do the throw
top = top.plus(safetyZoneSize);
inSafetyZone = true;
// This new will now be ok
if (Heap.verbose()) {
Log.println("Throwing OutOfMemoryError");
}
throw new OutOfMemoryError();
}
}
oldAllocationMark = allocationMark().asPointer();
cell = adjustForDebugTag ? DebugHeap.adjustForDebugTag(oldAllocationMark) : oldAllocationMark;
end = cell.plus(size);
}
} while (toSpace.mark.compareAndSwap(oldAllocationMark, end) != oldAllocationMark);
// Zero the allocated chunk before returning
Memory.clearWords(cell, size.dividedBy(Word.size()).toInt());
return cell;
}
/**
* Inserts {@linkplain DebugHeap#writeCellPadding(Pointer, int) padding} into the unused portion of a thread's TLAB.
*
* @param etla the pointer to the safepoint-enabled VM thread locals for the thread whose TLAB is
* to be padded
*/
static void padTLAB(Pointer etla, Pointer tlabMark, Pointer tlabTop) {
final int padWords = DebugHeap.writeCellPadding(tlabMark, tlabTop);
if (logTLAB()) {
final VmThread vmThread = UnsafeCast.asVmThread(VM_THREAD.loadRef(etla).toJava());
HeapSchemeWithTLAB.logger.logPad(vmThread, tlabMark, padWords);
}
}
public boolean contains(Address address) {
return toSpace.contains(address);
}
/**
* Verifies invariants for memory spaces (i.e. heap, code caches, thread stacks) that contain
* objects and/or object references.
*
* @param when a description of the current GC phase
*/
private void verifyObjectSpaces(GCCallbackPhase when) {
if (!MaxineVM.isDebug() && !VerifyReferences) {
return;
}
if (MaxineVM.isDebug()) {
zapRegion(fromSpace, when);
}
if (Heap.logGCPhases()) {
phaseLogger.logVerifyingObjectSpaces(Interval.BEGIN, when);
}
if (Heap.logGCPhases()) {
phaseLogger.logVerifyingStackReferences(Interval.BEGIN);
}
gcRootsVerifier.run();
if (Heap.logGCPhases()) {
phaseLogger.logVerifyingStackReferences(Interval.END);
}
if (MaxineVM.isDebug()) {
if (Heap.logGCPhases()) {
phaseLogger.logVerifyingHeapObjects(Interval.BEGIN);
phaseLogger.logVerifyingRegion(toSpace, toSpace.start().asPointer(), allocationMark());
}
DebugHeap.verifyRegion(toSpace, toSpace.start().asPointer(), allocationMark(), refVerifier, detailLogger);
if (Heap.logGCPhases()) {
phaseLogger.logVerifyingHeapObjects(Interval.END);
phaseLogger.logVerifyingCodeObjects(Interval.BEGIN);
}
verifyCodeRegion(Code.getCodeManager().getRuntimeBaselineCodeRegion());
verifyCodeRegion(Code.getCodeManager().getRuntimeOptCodeRegion());
if (Heap.logGCPhases()) {
phaseLogger.logVerifyingCodeObjects(Interval.END);
}
}
if (Heap.logGCPhases()) {
phaseLogger.logVerifyingObjectSpaces(Interval.END, when);
}
}
private void verifyCodeRegion(CodeRegion cr) {
if (!cr.size().isZero()) {
if (Heap.logGCPhases()) {
phaseLogger.logVerifyingRegion(cr, cr.start().asPointer(), cr.getAllocationMark());
}
DebugHeap.verifyRegion(cr, cr.start().asPointer(), cr.getAllocationMark(), refVerifier, detailLogger);
}
}
private void zapRegion(MemoryRegion region, GCCallbackPhase when) {
if (Heap.logGCPhases()) {
phaseLogger.logZappingRegion(region, when);
}
Memory.zapRegion(region);
}
private void logSpaces() {
if (Heap.verbose()) {
logSpace(fromSpace);
logSpace(toSpace);
Log.print("top "); Log.print(top);
Log.print(", allocation mark ");
Log.println(allocationMark());
}
}
private void logSpace(MemoryRegion space) {
Log.print(space.regionName());
Log.print(" start "); Log.print(space.start());
Log.print(", end "); Log.print(space.end());
Log.print(", size "); Log.print(space.size());
Log.println("");
}
/**
* Operation to shrink the heap.
*/
final class ShrinkHeap extends VmOperation {
boolean result;
final Size amount;
public ShrinkHeap(Size amount) {
super("ShrinkHeap", null, Mode.Safepoint);
this.amount = amount;
}
@Override
protected void doIt() {
final Size pageAlignedAmount = amount.asAddress().alignUp(Platform.platform().pageSize).asSize().dividedBy(2);
logSpaces();
executeGC();
if (immediateFreeSpace().greaterEqual(pageAlignedAmount)) {
// give back part of the existing spaces
if (Heap.verbose()) {
logSpaces();
}
final int amountAsInt = pageAlignedAmount.toInt();
fromSpace.setSize(fromSpace.size().minus(amountAsInt));
toSpace.setSize(toSpace.size().minus(amountAsInt));
top = top.minus(amountAsInt);
VirtualMemory.deallocate(fromSpace.end(), pageAlignedAmount, VirtualMemory.Type.HEAP);
VirtualMemory.deallocate(toSpace.end(), pageAlignedAmount, VirtualMemory.Type.HEAP);
logSpaces();
result = true;
}
}
}
@Override
public boolean decreaseMemory(Size amount) {
HeapScheme.Inspect.notifyDecreaseMemoryRequested(amount);
ShrinkHeap shrinkHeap = new ShrinkHeap(amount);
synchronized (HEAP_LOCK) {
shrinkHeap.submit();
}
return shrinkHeap.result;
}
final class GrowHeap extends VmOperation {
boolean result;
final Size amount;
public GrowHeap(Size amount) {
super("GrowHeap", null, Mode.Safepoint);
this.amount = amount;
}
@Override
protected void doIt() {
/* The conservative assumption is that "amount" is the total amount that we could
* allocate. Since we can't deallocate our existing spaces until we know we can allocate
* the new ones, our new spaces cannot be greater than amount/2 in size.
* This could be smaller than the existing spaces so we need to check.
* It's unfortunate but that's the nature of the semispace scheme.
*/
final Size pageAlignedAmount = amount.asAddress().alignUp(Platform.platform().pageSize).asSize().dividedBy(2);
if (pageAlignedAmount.greaterThan(fromSpace.size())) {
// grow adds the current space size to the amount in the grow policy
increaseGrowPolicy.setAmount(pageAlignedAmount.minus(fromSpace.size()));
result = grow(increaseGrowPolicy);
}
}
}
@Override
public boolean increaseMemory(Size amount) {
HeapScheme.Inspect.notifyIncreaseMemoryRequested(amount);
GrowHeap growHeap = new GrowHeap(amount);
synchronized (HEAP_LOCK) {
growHeap.submit();
}
return growHeap.result;
}
@Override
public void walkHeap(CallbackCellVisitor visitor) {
ImmortalHeap.visitCells(visitor);
Heap.bootHeapRegion.visitCells(visitor);
visitCells(visitor);
}
public boolean pin(Object object) {
return false;
}
public void unpin(Object object) {
throw new UnsupportedOperationException();
}
/**
* The policy for how to grow the heap.
*/
private abstract class GrowPolicy {
/**
* Returns the new size given the old.
* @param current the current size
* @return the new size
*/
abstract Size growth(Size current);
}
private class DoubleGrowPolicy extends GrowPolicy {
@Override
Size growth(Size current) {
return current.times(2);
}
}
private class LinearGrowPolicy extends GrowPolicy {
int amount;
LinearGrowPolicy(String s) {
amount = Heap.initialSize().toInt();
}
LinearGrowPolicy() {
}
@Override
Size growth(Size current) {
return current.plus(amount);
}
void setAmount(int amount) {
this.amount = amount;
}
void setAmount(Size amount) {
this.amount = amount.toInt();
}
}
@Override
public long maxObjectInspectionAge() {
return System.currentTimeMillis() - lastGCTime;
}
@Override
public GarbageCollectorMXBean getGarbageCollectorMXBean() {
return new SemiSpaceGarbageCollectorMXBean();
}
private final class SemiSpaceGarbageCollectorMXBean extends HeapSchemeAdaptor.GarbageCollectorMXBeanAdaptor {
private SemiSpaceGarbageCollectorMXBean() {
super("SemiSpace");
add(new SemiSpaceMemoryPoolMXBean(fromSpace, this));
add(new SemiSpaceMemoryPoolMXBean(toSpace, this));
}
}
private final class SemiSpaceMemoryPoolMXBean extends MemoryPoolMXBeanAdaptor {
SemiSpaceMemoryPoolMXBean(MemoryRegion region, MemoryManagerMXBean manager) {
super(MemoryType.HEAP, region, manager);
}
}
// Logging
static final PhaseLogger phaseLogger = new PhaseLogger();
@HOSTED_ONLY
@VMLoggerInterface(parent = HeapScheme.PhaseLogger.class)
private interface PhaseLoggerInterface {
void scanningThreadRoots(@VMLogParam(name = "vmThread") VmThread vmThread);
void scanningRoots(@VMLogParam(name = "interval") Interval interval);
void scanningBootHeap(@VMLogParam(name = "interval") Interval interval);
void scanningImmortalHeap(@VMLogParam(name = "interval") Interval interval);
void scanningCode(@VMLogParam(name = "interval") Interval interval);
void movingReachable(@VMLogParam(name = "interval") Interval interval);
void processingSpecialReferences(@VMLogParam(name = "interval") Interval interval);
void verifyingObjectSpaces(@VMLogParam(name = "interval") Interval interval, @VMLogParam(name = "when") GCCallbackPhase when);
void verifyingStackReferences(@VMLogParam(name = "interval") Interval interval);
void verifyingHeapObjects(@VMLogParam(name = "interval") Interval interval);
void verifyingCodeObjects(@VMLogParam(name = "interval") Interval interval);
void verifyingRegion(@VMLogParam(name = "region") MemoryRegion region, @VMLogParam(name = "start") Address start, @VMLogParam(name = "end") Address end);
void zappingRegion(@VMLogParam(name = "region") MemoryRegion region, @VMLogParam(name = "when") GCCallbackPhase when);
}
public static final class PhaseLogger extends PhaseLoggerAuto {
PhaseLogger() {
super(null, null);
}
@Override
protected void traceScanningBootHeap(Interval interval) {
tracePhase("Scanning boot heap", interval);
}
@Override
protected void traceScanningThreadRoots(VmThread vmThread) {
Log.print("Scanning thread local and stack roots for thread ");
Log.printThread(vmThread, true);
}
@Override
protected void traceScanningRoots(Interval interval) {
tracePhase("Scanning roots", interval);
}
@Override
protected void traceScanningImmortalHeap(Interval interval) {
tracePhase("Scanning immortal heap", interval);
}
@Override
protected void traceScanningCode(Interval interval) {
tracePhase("Scanning code", interval);
}
@Override
protected void traceMovingReachable(Interval interval) {
tracePhase("Moving reachable", interval);
}
@Override
protected void traceProcessingSpecialReferences(Interval interval) {
tracePhase("Processing special references", interval);
}
@Override
protected void traceVerifyingObjectSpaces(Interval interval, GCCallbackPhase when) {
Log.print(interval.name());
Log.print(": ");
Log.print("Verifying object spaces ");
Log.println(when.description);
}
@Override
protected void traceVerifyingStackReferences(Interval interval) {
tracePhase("Verifying stack references", interval);
}
@Override
protected void traceVerifyingHeapObjects(Interval interval) {
tracePhase("Verifying heap objects", interval);
}
@Override
protected void traceVerifyingCodeObjects(Interval interval) {
tracePhase("Verifying code objects", interval);
}
private static void tracePhase(String description, Interval interval) {
Log.print(interval.name()); Log.print(": "); Log.println(description);
}
@Override
protected void traceVerifyingRegion(MemoryRegion region, Address start, Address end) {
Log.print("Verifying region ");
Log.print(' ');
Log.print(region.regionName());
Log.print(' ');
Log.print(" [");
Log.print(start);
Log.print(" .. ");
Log.print(end);
Log.println(")");
}
@Override
protected void traceZappingRegion(MemoryRegion region, GCCallbackPhase when) {
Log.print("Zapping region ");
Log.print(' ');
Log.print(region.regionName());
Log.print(' ');
Log.println(when.description);
}
}
@Override
public PhaseLogger phaseLogger() {
return phaseLogger;
}
private static final TimeLogger timeLogger = new TimeLogger();
@HOSTED_ONLY
@VMLoggerInterface(parent = HeapScheme.TimeLogger.class)
private interface TimeLoggerInterface {
void stackReferenceMapPreparationTime(
@VMLogParam(name = "stackReferenceMapPreparationTime") long stackReferenceMapPreparationTime);
void phaseTimes(
@VMLogParam(name = "invocationCount") int invocationCount,
@VMLogParam(name = "clearTime") long clearTime,
@VMLogParam(name = "rootScanTime") long rootScanTime,
@VMLogParam(name = "bootHeapScanTime") long bootHeapScanTime,
@VMLogParam(name = "codeScanTime") long codeScanTime,
@VMLogParam(name = "copyTime") long copyTime,
@VMLogParam(name = "weakRefTime") long weakRefTime,
@VMLogParam(name = "gcTime") long gcTime);
}
public static final class TimeLogger extends TimeLoggerAuto {
private static final String HZ_SUFFIX = TimerUtil.getHzSuffix(HeapScheme.GC_TIMING_CLOCK);
private static final String TIMINGS_LEAD = "Timings (" + HZ_SUFFIX + ") for ";
TimeLogger() {
super(null, null);
}
@Override
protected void traceStackReferenceMapPreparationTime(long stackReferenceMapPreparationTime) {
Log.print("Stack reference map preparation time: ");
Log.print(stackReferenceMapPreparationTime);
Log.println(HZ_SUFFIX);
}
@Override
protected void tracePhaseTimes(int invocationCount, long clearTime, long rootScanTime, long bootHeapScanTime,
long codeScanTime, long copyTime, long weakRefTime, long gcTime) {
Log.print(TIMINGS_LEAD);
if (invocationCount < 0) {
Log.print("all GC");
} else {
Log.print("GC ");
Log.print(invocationCount);
}
Log.print(": clear & initialize=");
Log.print(clearTime);
Log.print(", root scan=");
Log.print(rootScanTime);
Log.print(", boot heap scan=");
Log.print(bootHeapScanTime);
Log.print(", code scan=");
Log.print(codeScanTime);
Log.print(", copy=");
Log.print(copyTime);
Log.print(", weak refs=");
Log.print(weakRefTime);
Log.print(", total=");
Log.println(gcTime);
}
/**
* Inspector use, mimic the trace.
* TODO use tool tips for this?
*/
@HOSTED_ONLY
@Override
public String inspectedArgValue(int op, int argNum, Word argValue) {
if (op == Operation.PhaseTimes.ordinal()) {
// Checkstyle: stop
switch (argNum) {
case 1: return "gc=" + argValue.asAddress().toInt();
case 2: return "init=" + argValue.asAddress().toLong();
case 3: return "rs=" + argValue.asAddress().toLong();
case 4: return "bs=" + argValue.asAddress().toLong();
case 5: return "cs=" + argValue.asAddress().toLong();
case 6: return "copy=" + argValue.asAddress().toLong();
case 7: return "srefs=" + argValue.asAddress().toLong();
case 8: return "total=" + argValue.asAddress().toLong();
default: return "???";
}
// Checkstyle: resume
}
return null;
}
}
@Override
public TimeLogger timeLogger() {
return timeLogger;
}
static final DebugHeap.DetailLogger detailLogger = new DebugHeap.DetailLogger();
// START GENERATED CODE
private static abstract class PhaseLoggerAuto extends com.sun.max.vm.heap.HeapScheme.PhaseLogger {
public enum Operation {
MovingReachable, ProcessingSpecialReferences, ScanningBootHeap,
ScanningCode, ScanningImmortalHeap, ScanningRoots, ScanningThreadRoots,
VerifyingCodeObjects, VerifyingHeapObjects, VerifyingObjectSpaces, VerifyingRegion,
VerifyingStackReferences, ZappingRegion;
@SuppressWarnings("hiding")
public static final Operation[] VALUES = values();
}
private static final int[] REFMAPS = new int[] {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1};
protected PhaseLoggerAuto(String name, String optionDescription) {
super(name, Operation.VALUES.length, optionDescription, REFMAPS);
}
@Override
public String operationName(int opCode) {
return Operation.VALUES[opCode].name();
}
@INLINE
public final void logMovingReachable(Interval interval) {
log(Operation.MovingReachable.ordinal(), intervalArg(interval));
}
protected abstract void traceMovingReachable(Interval interval);
@INLINE
public final void logProcessingSpecialReferences(Interval interval) {
log(Operation.ProcessingSpecialReferences.ordinal(), intervalArg(interval));
}
protected abstract void traceProcessingSpecialReferences(Interval interval);
@INLINE
public final void logScanningBootHeap(Interval interval) {
log(Operation.ScanningBootHeap.ordinal(), intervalArg(interval));
}
protected abstract void traceScanningBootHeap(Interval interval);
@INLINE
public final void logScanningCode(Interval interval) {
log(Operation.ScanningCode.ordinal(), intervalArg(interval));
}
protected abstract void traceScanningCode(Interval interval);
@INLINE
public final void logScanningImmortalHeap(Interval interval) {
log(Operation.ScanningImmortalHeap.ordinal(), intervalArg(interval));
}
protected abstract void traceScanningImmortalHeap(Interval interval);
@INLINE
public final void logScanningRoots(Interval interval) {
log(Operation.ScanningRoots.ordinal(), intervalArg(interval));
}
protected abstract void traceScanningRoots(Interval interval);
@Override
@INLINE
public final void logScanningThreadRoots(VmThread vmThread) {
log(Operation.ScanningThreadRoots.ordinal(), vmThreadArg(vmThread));
}
protected abstract void traceScanningThreadRoots(VmThread vmThread);
@INLINE
public final void logVerifyingCodeObjects(Interval interval) {
log(Operation.VerifyingCodeObjects.ordinal(), intervalArg(interval));
}
protected abstract void traceVerifyingCodeObjects(Interval interval);
@INLINE
public final void logVerifyingHeapObjects(Interval interval) {
log(Operation.VerifyingHeapObjects.ordinal(), intervalArg(interval));
}
protected abstract void traceVerifyingHeapObjects(Interval interval);
@INLINE
public final void logVerifyingObjectSpaces(Interval interval, GCCallbackPhase when) {
log(Operation.VerifyingObjectSpaces.ordinal(), intervalArg(interval), gCCallbackPhaseArg(when));
}
protected abstract void traceVerifyingObjectSpaces(Interval interval, GCCallbackPhase when);
@INLINE
public final void logVerifyingRegion(MemoryRegion region, Address start, Address end) {
log(Operation.VerifyingRegion.ordinal(), objectArg(region), start, end);
}
protected abstract void traceVerifyingRegion(MemoryRegion region, Address start, Address end);
@INLINE
public final void logVerifyingStackReferences(Interval interval) {
log(Operation.VerifyingStackReferences.ordinal(), intervalArg(interval));
}
protected abstract void traceVerifyingStackReferences(Interval interval);
@INLINE
public final void logZappingRegion(MemoryRegion region, GCCallbackPhase when) {
log(Operation.ZappingRegion.ordinal(), objectArg(region), gCCallbackPhaseArg(when));
}
protected abstract void traceZappingRegion(MemoryRegion region, GCCallbackPhase when);
@Override
protected void trace(Record r) {
switch (r.getOperation()) {
case 0: { //MovingReachable
traceMovingReachable(toInterval(r, 1));
break;
}
case 1: { //ProcessingSpecialReferences
traceProcessingSpecialReferences(toInterval(r, 1));
break;
}
case 2: { //ScanningBootHeap
traceScanningBootHeap(toInterval(r, 1));
break;
}
case 3: { //ScanningCode
traceScanningCode(toInterval(r, 1));
break;
}
case 4: { //ScanningImmortalHeap
traceScanningImmortalHeap(toInterval(r, 1));
break;
}
case 5: { //ScanningRoots
traceScanningRoots(toInterval(r, 1));
break;
}
case 6: { //ScanningThreadRoots
traceScanningThreadRoots(toVmThread(r, 1));
break;
}
case 7: { //VerifyingCodeObjects
traceVerifyingCodeObjects(toInterval(r, 1));
break;
}
case 8: { //VerifyingHeapObjects
traceVerifyingHeapObjects(toInterval(r, 1));
break;
}
case 9: { //VerifyingObjectSpaces
traceVerifyingObjectSpaces(toInterval(r, 1), toGCCallbackPhase(r, 2));
break;
}
case 10: { //VerifyingRegion
traceVerifyingRegion(toMemoryRegion(r, 1), toAddress(r, 2), toAddress(r, 3));
break;
}
case 11: { //VerifyingStackReferences
traceVerifyingStackReferences(toInterval(r, 1));
break;
}
case 12: { //ZappingRegion
traceZappingRegion(toMemoryRegion(r, 1), toGCCallbackPhase(r, 2));
break;
}
}
}
static MemoryRegion toMemoryRegion(Record r, int argNum) {
if (MaxineVM.isHosted()) {
return (MemoryRegion) ObjectArg.getArg(r, argNum);
} else {
return asMemoryRegion(toObject(r, argNum));
}
}
@INTRINSIC(UNSAFE_CAST)
private static native MemoryRegion asMemoryRegion(Object arg);
private static GCCallbackPhase toGCCallbackPhase(Record r, int argNum) {
return GCCallbackPhase.VALUES[r.getIntArg(argNum)];
}
private static Word gCCallbackPhaseArg(GCCallbackPhase enumType) {
return Address.fromInt(enumType.ordinal());
}
}
private static abstract class TimeLoggerAuto extends com.sun.max.vm.heap.HeapScheme.TimeLogger {
public enum Operation {
PhaseTimes, StackReferenceMapPreparationTime;
@SuppressWarnings("hiding")
public static final Operation[] VALUES = values();
}
private static final int[] REFMAPS = null;
protected TimeLoggerAuto(String name, String optionDescription) {
super(name, Operation.VALUES.length, optionDescription, REFMAPS);
}
@Override
public String operationName(int opCode) {
return Operation.VALUES[opCode].name();
}
@INLINE
public final void logPhaseTimes(int invocationCount, long clearTime, long rootScanTime, long bootHeapScanTime, long codeScanTime,
long copyTime, long weakRefTime, long gcTime) {
log(Operation.PhaseTimes.ordinal(), intArg(invocationCount), longArg(clearTime), longArg(rootScanTime), longArg(bootHeapScanTime), longArg(codeScanTime),
longArg(copyTime), longArg(weakRefTime), longArg(gcTime));
}
protected abstract void tracePhaseTimes(int invocationCount, long clearTime, long rootScanTime, long bootHeapScanTime, long codeScanTime,
long copyTime, long weakRefTime, long gcTime);
@Override
@INLINE
public final void logStackReferenceMapPreparationTime(long stackReferenceMapPreparationTime) {
log(Operation.StackReferenceMapPreparationTime.ordinal(), longArg(stackReferenceMapPreparationTime));
}
protected abstract void traceStackReferenceMapPreparationTime(long stackReferenceMapPreparationTime);
@Override
protected void trace(Record r) {
switch (r.getOperation()) {
case 0: { //PhaseTimes
tracePhaseTimes(toInt(r, 1), toLong(r, 2), toLong(r, 3), toLong(r, 4), toLong(r, 5), toLong(r, 6), toLong(r, 7), toLong(r, 8));
break;
}
case 1: { //StackReferenceMapPreparationTime
traceStackReferenceMapPreparationTime(toLong(r, 1));
break;
}
}
}
}
// END GENERATED CODE
}