/* * Copyright (c) 2011, 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.gcx; import static com.sun.max.vm.heap.gcx.EvacuationTimers.TIMED_OPERATION.*; import static com.sun.max.vm.heap.gcx.HeapFreeChunk.*; import com.sun.max.annotate.*; import com.sun.max.unsafe.*; import com.sun.max.vm.*; import com.sun.max.vm.MaxineVM.Phase; 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.debug.*; import com.sun.max.vm.heap.debug.DebugHeap.DetailLogger; import com.sun.max.vm.heap.debug.DebugHeap.ReferenceFinder; import com.sun.max.vm.heap.gcx.EvacuationTimers.TIMED_OPERATION; 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.reference.*; import com.sun.max.vm.runtime.*; import com.sun.max.vm.thread.*; import com.sun.max.vm.type.*; /** * Base class for evacuating objects of an evacuated area made of possibly discontinuous range of addresses. * The class provides common services. Sub-classes define what set of contiguous ranges delimit the evacuated area, * what set of contiguous ranges hold the evacuated objects whose references haven't been processed yet, and * the remembered set holdings references to the evacuated area. * */ public abstract class Evacuator extends PointerIndexVisitor implements CellVisitor, OverlappingCellVisitor, SpecialReferenceManager.GC { /** * Tells from what GC invocation should tracing of dirty card starts. */ public static int TraceFromGCInvocation = 0; private static boolean TraceEvacVisitedCell = false; private static boolean traceEvacVisitedCellEnabled = false; static { VMOptions.addFieldOption("-XX:", "TraceFromGCInvocation", Evacuator.class, "Tells from which GC invocation tracing starts from", Phase.PRISTINE); VMOptions.addFieldOption("-XX:", "TraceEvacVisitedCell", Evacuator.class, "Trace cells visited by the evacuator (Debug mode only)", Phase.PRISTINE); } @INLINE protected final boolean traceEvacVisitedCell() { return MaxineVM.isDebug() && traceEvacVisitedCellEnabled && detailLogger.enabled(); } private final SequentialHeapRootsScanner heapRootsScanner = new SequentialHeapRootsScanner(this); private boolean refDiscoveryEnabled = true; private GCOperation currentGCOperation; private EvacuationTimers timers; private TIMED_OPERATION currentEvacuationOperation; protected PhaseLogger phaseLogger; protected DetailLogger detailLogger; public void setGCOperation(GCOperation gcOperation) { currentGCOperation = gcOperation; if (MaxineVM.isDebug() && gcOperation != null) { traceEvacVisitedCellEnabled = TraceEvacVisitedCell && TraceFromGCInvocation <= gcOperation.invocationCount(); } } final public GCOperation getGCOperation() { return currentGCOperation; } private void updateSpecialReference(Pointer origin) { if (refDiscoveryEnabled) { SpecialReferenceManager.discoverSpecialReference(origin); } else { // Treat referent as strong reference. if (traceEvacVisitedCell()) { Log.print("Resurecting referent"); Log.print(origin.getReference(SpecialReferenceManager.referentIndex()).toOrigin()); Log.print(" from special ref "); Log.print(origin); Log.print(" + "); Log.println(SpecialReferenceManager.referentIndex()); } updateEvacuatedRef(origin, SpecialReferenceManager.referentIndex()); } } final void enableSpecialRefDiscovery() { refDiscoveryEnabled = true; } final void disableSpecialRefDiscovery() { refDiscoveryEnabled = false; } @INLINE private void updateReferenceArray(Pointer refArrayOrigin, final int firstIndex, final int length) { for (int index = firstIndex; index < length; index++) { updateEvacuatedRef(refArrayOrigin, index); } } private void updateReferenceArray(Pointer refArrayOrigin) { final int length = Layout.readArrayLength(refArrayOrigin) + Layout.firstElementIndex(); updateReferenceArray(refArrayOrigin, Layout.firstElementIndex(), length); } private void updateReferenceArray(Pointer refArrayOrigin, Address start, Address end) { final int endOfArrayIndex = Layout.readArrayLength(refArrayOrigin) + Layout.firstElementIndex(); final Address firstElementAddr = refArrayOrigin.plusWords(Layout.firstElementIndex()); final Address endOfArrayAddr = refArrayOrigin.plusWords(endOfArrayIndex); final int firstIndex = start.greaterThan(firstElementAddr) ? start.minus(refArrayOrigin).unsignedShiftedRight(Kind.REFERENCE.width.log2numberOfBytes).toInt() : Layout.firstElementIndex(); final int endIndex = endOfArrayAddr.greaterThan(end) ? end.minus(refArrayOrigin).unsignedShiftedRight(Kind.REFERENCE.width.log2numberOfBytes).toInt() : endOfArrayIndex; updateReferenceArray(refArrayOrigin, firstIndex, endIndex); } protected HeapRangeDumper dumper; protected DebugHeap.ReferenceFinder referenceFinder = new ReferenceFinder(false); protected Evacuator() { } public void setDumper(HeapRangeDumper dumper) { this.dumper = dumper; } public void setTimers(EvacuationTimers timers) { this.timers = timers; } /** * Set the phase logger for this evacuator. * HeapScheme using multiple evacuator instances might have to share a single phase logger * as currently the Heap class assume there's a single such phase logger per-heap scheme. * @param phaseLogger a phase logger */ public void setPhaseLogger(PhaseLogger phaseLogger) { this.phaseLogger = phaseLogger; } /** * Set the detail logger for this evacuator. * @param detailLogger a detail logger */ public void setDetailLogger(DetailLogger detailLogger) { this.detailLogger = detailLogger; } /** * Indicate whether the cell at the specified origin is in an area under evacuation. * @param origin origin of a cell * @return true if the cell is in an evacuation area */ abstract boolean inEvacuatedArea(Pointer origin); /** * Evacuate the cell at the specified origin. The destination of the cell is * @param origin origin of the cell to evacuate * @return origin of the cell after evacuation */ @NEVER_INLINE abstract Pointer evacuate(Pointer origin); /** * Remembered set updates to apply to a reference to an evacuated cell. * Default is to do nothing. * * @param refHolderOrigin origin of the reference holder * @param wordIndex word index relative to the reference holder's origin where the reference to the evacuated cell is stored. * @param ref reference to an evacuated cell */ void updateRSet(Pointer refHolderOrigin, int wordIndex, Reference ref) { // default is doing nothing. } /** * Evacuate a cell of the evacuated area if not already done, and return the reference to the evacuated cell new location. * * @param origin origin of the cell in the evacuated area * @return a reference to the evacuated cell's new location */ protected final Reference getForwardRef(Pointer origin) { Reference forwardRef = Layout.readForwardRef(origin); if (forwardRef.isZero()) { final Pointer toOrigin = evacuate(origin); forwardRef = Reference.fromOrigin(toOrigin); Layout.writeForwardRef(origin, forwardRef); } return forwardRef; } @NEVER_INLINE private void reportDarkMatterForwarding(Pointer origin, Pointer at, Pointer forwarded) { final boolean lockDisabledSafepoints = Log.lock(); Log.print("forwarding dark matter @ "); Log.print(at); Log.print(" from "); Log.print(origin); Log.print(" to "); Log.println(forwarded); Log.unlock(lockDisabledSafepoints); FatalError.unexpected("must not forward dark matter"); } /** * Evacuate a cell of the evacuated area if not already done, and return the reference to the evacuated cell new location. * Same as {@link #getForwardRef(Pointer)}, but with logging support. * The second argument is only used when detail logging is enabled (only in debug mode). * * @param origin origin of the cell in the evacuated area * @param at pointer to the location of the reference * @return a reference to the evacuated cell's new location */ protected final Reference getForwardRef(Pointer origin, Pointer at) { Reference forwardRef = Layout.readForwardRef(origin); if (forwardRef.isZero()) { final Pointer toOrigin = evacuate(origin); forwardRef = Reference.fromOrigin(toOrigin); Layout.writeForwardRef(origin, forwardRef); if (MaxineVM.isDebug()) { Reference hubRef = Layout.readHubReference(forwardRef); if (DarkMatter.isDarkMatterHub(hubRef.toOrigin())) { reportDarkMatterForwarding(origin, at, forwardRef.toOrigin()); } if (detailLogger.enabled()) { final Hub hub = UnsafeCast.asHub(hubRef.toJava()); detailLogger.logForward(hub.classActor.id, at, origin, toOrigin, Layout.size(toOrigin).toInt()); } } } return forwardRef; } /** * Test if a reference in an cell points to the evacuated area. If it does, the referenced cell is * first evacuated if it is still in the evacuated area. * The reference is updated to the evacuated cell's new location. * @param refHolderOrigin origin of the holder of the reference * @param wordIndex index to a reference of the evacuated cell. */ final void updateEvacuatedRef(Pointer refHolderOrigin, int wordIndex) { final Reference ref = refHolderOrigin.getReference(wordIndex); final Pointer origin = ref.toOrigin(); if (inEvacuatedArea(origin)) { final Reference forwardRef = MaxineVM.isDebug() ? getForwardRef(origin, refHolderOrigin.plusWords(wordIndex)) : getForwardRef(origin); refHolderOrigin.setReference(wordIndex, forwardRef); updateRSet(refHolderOrigin, wordIndex, forwardRef); } } /** * Apply the evacuation logic to the reference at the specified index from the origin of a cell. * @param origin origin of the cell holding the visited reference * @param wordIndex index of the visited reference from the origin */ @Override public void visit(Pointer origin, int wordIndex) { updateEvacuatedRef(origin, wordIndex); } @Override public boolean isReachable(Reference ref) { final Pointer origin = ref.toOrigin(); if (inEvacuatedArea(origin)) { return !Layout.readForwardRef(origin).isZero(); } return true; } @Override public Reference preserve(Reference ref) { final Pointer origin = ref.toOrigin(); if (inEvacuatedArea(origin)) { return getForwardRef(origin); } return ref; } @Override public boolean mayRelocateLiveObjects() { return true; } /** * Evacuate all objects of the evacuated area directly reachable from roots (thread stacks, monitors, etc.). */ void evacuateFromRoots() { heapRootsScanner.run(); } /** * Evacuate all objects of the evacuated area directly reachable from the remembered sets of the evacuated area. By default, this does nothing * (i.e., there are no remembered sets). For instance, a pure semi-space flat heap doesn't have any remembered sets to evacuate from, neither does * a old semi-space old generation. */ protected void evacuateFromRSets() { } /** * Evacuate all objects of the evacuated area reachable from already evacuated cells. */ abstract protected void evacuateReachables(); /** * Action to performed before evacuation begins, e.g., making evacuated space iterable, etc, initializing allocation buffers, etc. */ abstract protected void doBeforeEvacuation(); /** * Action to performed once evacuation is complete, e.g., releasing or making allocation buffers iterable, zero-ing out evacuated regions, etc. */ abstract protected void doAfterEvacuation(); /** * Action to performed before a GC on the heap space where objects are evacuated begins. */ public void doBeforeGC() { } /** * Action to performed after a GC on the heap space where objects are evacuated begins. */ public void doAfterGC() { } /** * Evacuate all objects of the evacuated area directly reachable from the boot heap. */ protected void evacuateFromBootHeap() { Heap.bootHeapRegion.visitReferences(this); } /** * Evacuate all objects of the evacuated area directly reachable from the immortal heap. */ final protected void evacuateFromImmortalHeap() { ImmortalHeap.visitCells(this); } void evacuateFromCode() { // 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); } /** * Scan a cell to evacuate the cells in the evacuation area it refers to and update its references to already evacuated cells. * @param cell */ @INLINE final protected Pointer scanCellForEvacuatees(Pointer cell) { if (traceEvacVisitedCell()) { 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 updateEvacuatedRef(origin, Layout.hubIndex()); final Hub hub = Layout.getHub(origin); if (hub == heapFreeChunkHub()) { return cell.plus(toHeapFreeChunk(origin).size); } // Update the other references in the object final SpecificLayout specificLayout = hub.specificLayout; if (specificLayout == Layout.tupleLayout()) { //TupleReferenceMap.visitReferences(hub, origin, this); hub.visitMappedReferences(origin, this); if (hub.isJLRReference) { updateSpecialReference(origin); } return cell.plus(hub.tupleSize); } final int length = Layout.readArrayLength(origin); if (specificLayout == Layout.hybridLayout()) { hub.visitMappedReferences(origin, this); //TupleReferenceMap.visitReferences(hub, origin, this); return cell.plus(Layout.hybridLayout().getArraySize(length)); } else if (specificLayout == Layout.referenceArrayLayout()) { updateReferenceArray(origin); return cell.plus(Layout.referenceArrayLayout().getArraySize(Kind.REFERENCE, length)); } return cell.plus(Layout.size(origin)); // cell.plus(Layout.arrayLayout().getArraySize(length)); } private boolean cellInRegion(Pointer cell, Pointer endOfCell, Address start, Address end) { return cell.greaterEqual(start) && endOfCell.lessEqual(end); } private void checkCellOverlap(Pointer cell, Address start, Address end) { if (MaxineVM.isDebug()) { final Pointer origin = Layout.cellToOrigin(cell); final boolean isHeapFreeChunk = HeapFreeChunk.isHeapFreeChunkOrigin(origin); final Pointer endOfCell = isHeapFreeChunk ? cell.plus(HeapFreeChunk.getFreechunkSize(cell)) : cell.plus(Layout.size(origin)); if ((cell.lessThan(end) && endOfCell.greaterThan(start)) || cellInRegion(cell, endOfCell, start, end)) { return; } Log.print(isHeapFreeChunk ? "Free Chunk [" : "Cell ["); Log.print(cell); Log.print(','); Log.print(endOfCell); Log.print("] don't overlap range ["); Log.print(start); Log.print(", "); Log.print(end); Log.println("]"); FatalError.check(false, "Cell doesn't overlap range"); } } /** * Scan the part of cell that overlap with a region of memory to evacuate the cells in the evacuation area it refers to and update its references * to already evacuated cells. * * @param cell Pointer to the first word of the cell to be visited * @param start start of the region overlapping with the cell * @param end end of the region overlapping with the cell * @return pointer to the end of the cell */ final protected Pointer scanCellForEvacuatees(Pointer cell, Address start, Address end) { checkCellOverlap(cell, start, end); final Pointer origin = Layout.cellToOrigin(cell); Pointer hubReferencePtr = origin.plus(Layout.hubIndex()); if (hubReferencePtr.greaterEqual(start)) { updateEvacuatedRef(origin, Layout.hubIndex()); } final Hub hub = UnsafeCast.asHub(origin.getReference(Layout.hubIndex())); if (hub == heapFreeChunkHub()) { return cell.plus(toHeapFreeChunk(origin).size); } // Update the other references in the object final SpecificLayout specificLayout = hub.specificLayout; if (specificLayout == Layout.tupleLayout()) { // Visit all the references of the object and not just those over the range. // This is because the write barrier dirty the card holding the tuple header, not // the actually modified reference. Thus bounding the iteration to the dirty card might // miss references on the next card. //TupleReferenceMap.visitReferences(hub, origin, this); hub.visitMappedReferences(origin, this); if (hub.isJLRReference) { updateSpecialReference(origin); } return cell.plus(hub.tupleSize); } if (specificLayout == Layout.referenceArrayLayout()) { updateReferenceArray(origin, start, end); } else if (specificLayout == Layout.hybridLayout()) { hub.visitMappedReferences(origin, this); // See comment above // TupleReferenceMap.visitReferences(hub, origin, this); } return cell.plus(Layout.size(origin)); } // temp debug support private Pointer scannedCell = Pointer.zero(); /** * Evacuate all cells from the evacuated area reachable from the specified range of heap addresses. * The range comprise an integral number of cells. * * @param start first address of the range, must coincide with the start of a cell * @param end last address of the range, must coincide with the end of a cell */ final void evacuateRange(Pointer start, Pointer end) { /* Pointer */ scannedCell = start; while (scannedCell.lessThan(end)) { scannedCell = scanCellForEvacuatees(scannedCell); } } public TIMED_OPERATION currentEvacuationOperation() { return currentEvacuationOperation; } protected void doAfterOperation(TIMED_OPERATION op) { } public final void evacuate(boolean logPhases) { currentEvacuationOperation = PROLOGUE; timers.start(PROLOGUE); doBeforeEvacuation(); timers.stop(PROLOGUE); doAfterOperation(PROLOGUE); if (logPhases) { phaseLogger.logScanningRoots(VMLogger.Interval.BEGIN); } currentEvacuationOperation = ROOT_SCAN; timers.start(ROOT_SCAN); evacuateFromRoots(); timers.stop(ROOT_SCAN); doAfterOperation(ROOT_SCAN); if (logPhases) { phaseLogger.logScanningRoots(VMLogger.Interval.END); } if (logPhases) { phaseLogger.logScanningBootHeap(VMLogger.Interval.BEGIN); } currentEvacuationOperation = BOOT_HEAP_SCAN; timers.start(BOOT_HEAP_SCAN); evacuateFromBootHeap(); timers.stop(BOOT_HEAP_SCAN); doAfterOperation(BOOT_HEAP_SCAN); if (logPhases) { phaseLogger.logScanningBootHeap(VMLogger.Interval.END); } if (logPhases) { phaseLogger.logScanningCode(VMLogger.Interval.BEGIN); } currentEvacuationOperation = CODE_SCAN; timers.start(CODE_SCAN); evacuateFromCode(); timers.stop(CODE_SCAN); doAfterOperation(CODE_SCAN); if (logPhases) { phaseLogger.logScanningCode(VMLogger.Interval.END); } if (Heap.logGCPhases()) { phaseLogger.logScanningImmortalHeap(VMLogger.Interval.BEGIN); } currentEvacuationOperation = IMMORTAL_SCAN; timers.start(IMMORTAL_SCAN); evacuateFromImmortalHeap(); timers.stop(IMMORTAL_SCAN); doAfterOperation(IMMORTAL_SCAN); if (Heap.logGCPhases()) { phaseLogger.logScanningImmortalHeap(VMLogger.Interval.END); } if (logPhases) { phaseLogger.logScanningRSet(VMLogger.Interval.BEGIN); } currentEvacuationOperation = RSET_SCAN; timers.start(RSET_SCAN); evacuateFromRSets(); timers.stop(RSET_SCAN); doAfterOperation(RSET_SCAN); if (logPhases) { phaseLogger.logScanningRSet(VMLogger.Interval.END); } if (logPhases) { phaseLogger.logEvacuating(VMLogger.Interval.BEGIN); } currentEvacuationOperation = COPY; timers.start(COPY); evacuateReachables(); timers.stop(COPY); doAfterOperation(COPY); if (logPhases) { phaseLogger.logEvacuating(VMLogger.Interval.END); } if (logPhases) { phaseLogger.logProcessingSpecialReferences(VMLogger.Interval.BEGIN); } currentEvacuationOperation = WEAK_REF; timers.start(WEAK_REF); disableSpecialRefDiscovery(); SpecialReferenceManager.processDiscoveredSpecialReferences(this); evacuateReachables(); enableSpecialRefDiscovery(); timers.stop(WEAK_REF); doAfterOperation(WEAK_REF); if (logPhases) { phaseLogger.logProcessingSpecialReferences(VMLogger.Interval.END); } currentEvacuationOperation = EPILOGUE; timers.start(EPILOGUE); doAfterEvacuation(); timers.stop(EPILOGUE); doAfterOperation(EPILOGUE); } /** * Scan a cell to evacuate the cells in the evacuation area it refers to and update its references to already evacuated cells. * * @param cell a pointer to a cell * @return pointer to the end of the cell */ public final Pointer visitCell(Pointer cell) { return scanCellForEvacuatees(cell); } /** * Scan a cell to evacuate the cells in the evacuation area it refers to and update its references to already evacuated cells. * * @param cell a pointer to a cell * @return pointer to the end of the cell */ public final Pointer visitCell(Pointer cell, Address start, Address end) { return scanCellForEvacuatees(cell, start, end); } @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 scanningCode(@VMLogParam(name = "interval") Interval interval); void scanningImmortalHeap(@VMLogParam(name = "interval") Interval interval); void scanningRSet(@VMLogParam(name = "interval") Interval interval); void evacuating(@VMLogParam(name = "interval") Interval interval); void processingSpecialReferences(@VMLogParam(name = "interval") Interval interval); } public static final class PhaseLogger extends PhaseLoggerAuto { private static void tracePhase(String description, Interval interval) { Log.print(interval.name()); Log.print(": "); Log.println(description); } public PhaseLogger() { super(null, null); } @Override protected void traceEvacuating(Interval interval) { tracePhase("Evacuating reachables", interval); } @Override protected void traceProcessingSpecialReferences(Interval interval) { tracePhase("Processing special references", interval); } @Override protected void traceScanningBootHeap(Interval interval) { tracePhase("Scanning boot heap", interval); } @Override protected void traceScanningCode(Interval interval) { tracePhase("Scanning code", interval); } @Override protected void traceScanningImmortalHeap(Interval interval) { tracePhase("Scanning immortal heap", interval); } @Override protected void traceScanningRSet(Interval interval) { tracePhase("Scanning remembered sets", interval); } @Override protected void traceScanningRoots(Interval interval) { tracePhase("Scanning roots", interval); } @Override protected void traceScanningThreadRoots(VmThread vmThread) { Log.print("Scanning thread local and stack roots for thread "); Log.printThread(vmThread, true); } } // START GENERATED CODE private static abstract class PhaseLoggerAuto extends com.sun.max.vm.heap.HeapScheme.PhaseLogger { public enum Operation { Evacuating, ProcessingSpecialReferences, ScanningBootHeap, ScanningCode, ScanningImmortalHeap, ScanningRSet, ScanningRoots, ScanningThreadRoots; @SuppressWarnings("hiding") public static final Operation[] VALUES = values(); } private static final int[] REFMAPS = null; 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 logEvacuating(Interval interval) { log(Operation.Evacuating.ordinal(), intervalArg(interval)); } protected abstract void traceEvacuating(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 logScanningRSet(Interval interval) { log(Operation.ScanningRSet.ordinal(), intervalArg(interval)); } protected abstract void traceScanningRSet(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); @Override protected void trace(Record r) { switch (r.getOperation()) { case 0: { //Evacuating traceEvacuating(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: { //ScanningRSet traceScanningRSet(toInterval(r, 1)); break; } case 6: { //ScanningRoots traceScanningRoots(toInterval(r, 1)); break; } case 7: { //ScanningThreadRoots traceScanningThreadRoots(toVmThread(r, 1)); break; } } } } // END GENERATED CODE }