/** * This abstract class implements the core functionality of generic two-generationa copying * collectors. Nursery collections occur when either the heap is full or the nursery is full. The * nursery size is determined by an optional command line argument. If undefined, the nursery size * is "infinite", so nursery collections only occur when the heap is full (this is known as a * flexible-sized nursery collector). Thus both fixed and flexible nursery sizes are supported. Full * heap collections occur when the nursery size has dropped to a statically defined threshold, * <code>NURSERY_THRESHOLD</code> * * <p>See also Plan.java for general comments on local vs global plan classes. */ @Uninterruptible public abstract class Gen extends StopTheWorld { /** * *************************************************************************** * * <p>Constants */ protected static final float SURVIVAL_ESTIMATE = 0.8f; // est yield protected static final float MATURE_FRACTION = 0.5f; // est yield private static final float WORST_CASE_COPY_EXPANSION = 1.5f; // worst case for addition of one word overhead due to address based hashing public static final boolean IGNORE_REMSETS = false; public static final boolean USE_NON_HEAP_OBJECT_REFERENCE_WRITE_BARRIER = true; public static final boolean USE_OBJECT_BARRIER_FOR_AASTORE = false; // choose between slot and object barriers public static final boolean USE_OBJECT_BARRIER_FOR_PUTFIELD = false; // choose between slot and object barriers public static final boolean USE_OBJECT_BARRIER = USE_OBJECT_BARRIER_FOR_AASTORE || USE_OBJECT_BARRIER_FOR_PUTFIELD; /** Fraction of available virtual memory to give to the nursery (if contiguous) */ protected static final float NURSERY_VM_FRACTION = 0.15f; /** Switch between a contiguous and discontiguous nursery (experimental) */ static final boolean USE_DISCONTIGUOUS_NURSERY = false; // Allocators public static final int ALLOC_NURSERY = ALLOC_DEFAULT; public static final int ALLOC_MATURE = StopTheWorld.ALLOCATORS + 1; public static final int ALLOC_MATURE_MINORGC = StopTheWorld.ALLOCATORS + 2; public static final int ALLOC_MATURE_MAJORGC = StopTheWorld.ALLOCATORS + 3; public static final int SCAN_NURSERY = 0; public static final int SCAN_MATURE = 1; /** * *************************************************************************** * * <p>Class fields */ /* Statistics */ protected static final BooleanCounter fullHeap = new BooleanCounter("majorGC", true, true); private static final Timer fullHeapTime = new Timer("majorGCTime", false, true); protected static final EventCounter wbFast; protected static final EventCounter wbSlow; public static final SizeCounter nurseryMark; public static final SizeCounter nurseryCons; /** The nursery space is where all new objects are allocated by default */ private static final VMRequest vmRequest = USE_DISCONTIGUOUS_NURSERY ? VMRequest.create() : VMRequest.create(NURSERY_VM_FRACTION, true); public static final CopySpace nurserySpace = new CopySpace("nursery", DEFAULT_POLL_FREQUENCY, false, vmRequest); public static final int NURSERY = nurserySpace.getDescriptor(); private static final Address NURSERY_START = nurserySpace.getStart(); /** * *************************************************************************** * * <p>Instance fields */ /* status fields */ public boolean gcFullHeap = false; public boolean nextGCFullHeap = false; /* The trace object */ public final Trace nurseryTrace = new Trace(metaDataSpace); /** Remset pools */ public final SharedDeque modbufPool = new SharedDeque("modBufs", metaDataSpace, 1); public final SharedDeque remsetPool = new SharedDeque("remSets", metaDataSpace, 1); public final SharedDeque arrayRemsetPool = new SharedDeque("arrayRemSets", metaDataSpace, 2); /* * Class initializer */ static { if (GATHER_WRITE_BARRIER_STATS) { wbFast = new EventCounter("wbFast"); wbSlow = new EventCounter("wbSlow"); } else { wbFast = null; wbSlow = null; } if (Stats.GATHER_MARK_CONS_STATS) { nurseryMark = new SizeCounter("nurseryMark", true, true); nurseryCons = new SizeCounter("nurseryCons", true, true); } else { nurseryMark = null; nurseryCons = null; } } /** * *************************************************************************** * * <p>Collection */ /** Force the next collection to be full heap. */ public void forceFullHeapCollection() { nextGCFullHeap = true; } /** * Perform a (global) collection phase. * * @param phaseId Collection phase to execute. */ @NoInline public void collectionPhase(short phaseId) { if (phaseId == SET_COLLECTION_KIND) { super.collectionPhase(phaseId); gcFullHeap = requiresFullHeapCollection(); return; } if (phaseId == PREPARE) { nurserySpace.prepare(true); if (traceFullHeap()) { if (gcFullHeap) { if (Stats.gatheringStats()) fullHeap.set(); fullHeapTime.start(); } super.collectionPhase(phaseId); // we can throw away the remsets (but not modbuf) for a full heap GC remsetPool.clearDeque(1); arrayRemsetPool.clearDeque(2); } return; } if (phaseId == CLOSURE) { if (!traceFullHeap()) { nurseryTrace.prepare(); } return; } if (phaseId == RELEASE) { nurserySpace.release(); modbufPool.clearDeque(1); remsetPool.clearDeque(1); arrayRemsetPool.clearDeque(2); if (!traceFullHeap()) { nurseryTrace.release(); } else { super.collectionPhase(phaseId); if (gcFullHeap) fullHeapTime.stop(); } nextGCFullHeap = (getPagesAvail() < Options.nurserySize.getMinNursery()); return; } super.collectionPhase(phaseId); } /** * This method controls the triggering of a GC. It is called periodically during allocation. * Returns true to trigger a collection. * * @param spaceFull Space request failed, must recover pages within 'space'. * @return True if a collection is requested by the plan. */ public final boolean collectionRequired(boolean spaceFull) { int nurseryPages = nurserySpace.reservedPages(); if (nurseryPages > Options.nurserySize.getMaxNursery()) { return true; } if (virtualMemoryExhausted()) return true; return super.collectionRequired(spaceFull); } /** * Determine if this GC should be a full heap collection. * * @return True is this GC should be a full heap collection. */ protected boolean requiresFullHeapCollection() { if (collectionTrigger == Collection.EXTERNAL_GC_TRIGGER && Options.fullHeapSystemGC.getValue()) { return true; } if (nextGCFullHeap || collectionAttempt > 1) { // Forces full heap collection return true; } if (loSpace.allocationFailed() || nonMovingSpace.allocationFailed() || (USE_CODE_SPACE && (largeCodeSpace.allocationFailed() || smallCodeSpace.allocationFailed()))) { // We need space from the nursery return true; } if (virtualMemoryExhausted()) return true; int smallNurseryPages = nurserySpace.committedPages(); int smallNurseryYield = (int) ((smallNurseryPages << 1) * SURVIVAL_ESTIMATE); if (smallNurseryYield < getPagesRequired()) { // Our total yield is insufficent. return true; } if (nurserySpace.allocationFailed()) { if (smallNurseryYield < (nurserySpace.requiredPages() << 1)) { // We have run out of VM pages in the nursery return true; } } return false; } /** * Independent of how many pages remain in the page budget (a function of heap size), we must * ensure we never exhaust virtual memory. Therefore we must never let the nursery grow to the * extent that it can't be copied into the mature space. * * @return True if the nursery has grown to the extent that it may not be able to be copied into * the mature space. */ private boolean virtualMemoryExhausted() { return ((int) (nurserySpace.reservedPages() * WORST_CASE_COPY_EXPANSION) >= getMaturePhysicalPagesAvail()); } /** * *************************************************************************** * * <p>Correctness */ /** * *************************************************************************** * * <p>Accounting */ /** * Return the number of pages in use given the pending allocation. Simply add the nursery's * contribution to that of the superclass. * * @return The number of pages reserved given the pending allocation, excluding space reserved for * copying. */ @Override public int getPagesUsed() { return (nurserySpace.reservedPages() + super.getPagesUsed()); } /** * Return the number of pages available for allocation, <i>assuming all future allocation is to * the nursery</i>. * * @return The number of pages available for allocation, <i>assuming all future allocation is to * the nursery</i>. */ @Override public int getPagesAvail() { return super.getPagesAvail() >> 1; } /** * Return the number of pages reserved for copying. * * @return The number of pages reserved given the pending allocation, including space reserved for * copying. */ @Override public int getCollectionReserve() { return nurserySpace.reservedPages() + super.getCollectionReserve(); } /** * Return the number of pages available for allocation into the mature space. * * @return The number of pages available for allocation into the mature space. */ public abstract int getMaturePhysicalPagesAvail(); /** * Calculate the number of pages a collection is required to free to satisfy outstanding * allocation requests. * * @return the number of pages a collection is required to free to satisfy outstanding allocation * requests. */ @Override public int getPagesRequired() { /* We don't currently pretenure, so mature space must be zero */ return super.getPagesRequired() + (nurserySpace.requiredPages() << 1); } /** * *************************************************************************** * * <p>Miscellaneous */ /** * Return true if the address resides within the nursery * * @param addr The object to be tested * @return true if the address resides within the nursery */ @Inline static boolean inNursery(Address addr) { if (USE_DISCONTIGUOUS_NURSERY) return Map.getDescriptorForAddress(addr) == NURSERY; else return addr.GE(NURSERY_START); } /** * Return true if the object resides within the nursery * * @param obj The object to be tested * @return true if the object resides within the nursery */ @Inline static boolean inNursery(ObjectReference obj) { return inNursery(obj.toAddress()); } /** @return Does the mature space do copying ? */ protected boolean copyMature() { return false; } /** * Print pre-collection statistics. In this class we prefix the output indicating whether the * collection was full heap or not. */ public void printPreStats() { if ((Options.verbose.getValue() >= 1) && (gcFullHeap)) Log.write("[Full heap]"); super.printPreStats(); } /** @return The mature space, set by each subclass of <code>Gen</code>. */ protected abstract Space activeMatureSpace(); /** * @return True if we should trace the whole heap during collection. True if we're ignorning * remsets or if we're doing a full heap GC. */ public final boolean traceFullHeap() { return IGNORE_REMSETS || gcFullHeap; } /** @return Is current GC only collecting objects allocated since last GC. */ public final boolean isCurrentGCNursery() { return !gcFullHeap; } /** @return Is last GC a full collection? */ public final boolean lastCollectionFullHeap() { return gcFullHeap; } /** * @see org.mmtk.plan.Plan#willNeverMove * @param object Object in question * @return True if the object will never move */ @Override public boolean willNeverMove(ObjectReference object) { if (Space.isInSpace(NURSERY, object)) return false; return super.willNeverMove(object); } /** * Return the expected reference count. For non-reference counting collectors this becomes a * true/false relationship. * * @param object The object to check. * @param sanityRootRC The number of root references to the object. * @return The expected (root excluded) reference count. */ public int sanityExpectedRC(ObjectReference object, int sanityRootRC) { Space space = Space.getSpaceForObject(object); // Nursery if (space == Gen.nurserySpace) { return SanityChecker.DEAD; } // Immortal spaces if (space == Gen.immortalSpace || space == Gen.vmSpace) { return space.isReachable(object) ? SanityChecker.ALIVE : SanityChecker.DEAD; } // Mature space (nursery collection) if (VM.activePlan.global().isCurrentGCNursery()) { return SanityChecker.UNSURE; } // Mature space (full heap collection) return space.isReachable(object) ? SanityChecker.ALIVE : SanityChecker.DEAD; } /** Register specialized methods. */ @Interruptible protected void registerSpecializedMethods() { TransitiveClosure.registerSpecializedScan(SCAN_NURSERY, GenNurseryTraceLocal.class); super.registerSpecializedMethods(); } }
/** * This class implements the global state of a simple reference counting collector. * * <p>All plans make a clear distinction between <i>global</i> and <i>thread-local</i> activities, * and divides global and local state into separate class hierarchies. Global activities must be * synchronized, whereas no synchronization is required for thread-local activities. There is a * single instance of Plan (or the appropriate sub-class), and a 1:1 mapping of PlanLocal to "kernel * threads" (aka CPUs or in Jikes RVM, Processors). Thus instance methods of PlanLocal allow fast, * unsychronized access to functions such as allocation and collection. * * <p>The global instance defines and manages static resources (such as memory and virtual memory * resources). This mapping of threads to instances is crucial to understanding the correctness and * performance properties of MMTk plans. */ @Uninterruptible public class GenRC extends RCBase { /** * ************************************************************************** * * <p>Class variables */ /** The nursery space, where all new objects are allocated by default. */ public static CopySpace nurserySpace = new CopySpace("nursery", DEFAULT_POLL_FREQUENCY, false, VMRequest.create(0.15f, true)); public static final int NS = nurserySpace.getDescriptor(); // Allocators public static final int ALLOC_NURSERY = ALLOC_DEFAULT; /** * ************************************************************************** Instance variables */ /** Constructor. */ public GenRC() { if (VM.VERIFY_ASSERTIONS) { VM.assertions._assert(WITH_COALESCING_RC); } } /** * *************************************************************************** * * <p>Collection */ /** * Perform a (global) collection phase. * * @param phaseId Collection phase to execute. */ @NoInline public void collectionPhase(short phaseId) { if (phaseId == PREPARE) { nurserySpace.prepare(true); } if (phaseId == RELEASE) { nurserySpace.release(); } super.collectionPhase(phaseId); } /** * This method controls the triggering of a GC. It is called periodically during allocation. * Returns true to trigger a collection. * * @param spaceFull Space request failed, must recover pages within 'space'. * @return True if a collection is requested by the plan. */ public final boolean collectionRequired(boolean spaceFull) { boolean nurseryFull = nurserySpace.reservedPages() > Options.nurserySize.getMaxNursery(); return super.collectionRequired(spaceFull) || nurseryFull; } /** * Return the number of pages available for allocation, <i>assuming all future allocation is to * the nursery</i>. * * @return The number of pages available for allocation, <i>assuming all future allocation is to * the nursery</i>. */ public int getPagesAvail() { return super.getPagesAvail() >> 1; } /** * Return the number of pages reserved for copying. * * @return The number of pages reserved given the pending allocation, including space reserved for * copying. */ public final int getCollectionReserve() { return nurserySpace.reservedPages() + super.getCollectionReserve(); } /** * Return the number of pages in use given the pending allocation. Simply add the nursery's * contribution to that of the superclass. * * @return The number of pages reserved given the pending allocation, excluding space reserved for * copying. */ public final int getPagesUsed() { return super.getPagesUsed() + nurserySpace.reservedPages(); } /** * Calculate the number of pages a collection is required to free to satisfy outstanding * allocation requests. * * @return the number of pages a collection is required to free to satisfy outstanding allocation * requests. */ public int getPagesRequired() { return super.getPagesRequired() + (nurserySpace.requiredPages() << 1); } /** * @see org.mmtk.plan.Plan#willNeverMove * @param object Object in question * @return True if the object will never move */ @Override public boolean willNeverMove(ObjectReference object) { if (Space.isInSpace(NS, object)) return false; return super.willNeverMove(object); } }