// private creator methods for the streams @Interruptible private ShortStream createRemsetStream() { return VM.newGCspyShortStream( this, "Remembered set stream", (short) 0, // Say, typical size = 4 * typical scalar size? (short) (maxObjectsPerBlock(blockSize) / 8), (short) 0, (short) 0, "Remset references: ", " references", StreamConstants.PRESENTATION_PLUS, StreamConstants.PAINT_STYLE_ZERO, 0, Color.Cyan, true); }
/** * Constructor * * @param space The space to which this resource is attached */ private PageResource(Space space, boolean contiguous) { this.contiguous = contiguous; this.space = space; lock = VM.newLock(space.getName() + ".lock"); }
/** * A garbage collection proceeds as a sequence of phases. Each phase is either simple (singular) or * complex (an array). * * <p>The context an individual phase executes in may be global, mutator, or collector. * * <p>Phases are executed within a stack and all synchronization between parallel GC threads is * managed from within this class. * * @see CollectorContext#collectionPhase * @see MutatorContext#collectionPhase * @see Plan#collectionPhase */ @Uninterruptible public abstract class Phase implements Constants { /** * ********************************************************************* * * <p>Phase allocation and storage. */ /** The maximum number of phases */ private static final int MAX_PHASES = 64; /** The array of phase instances. Zero is unused. */ private static final Phase[] phases = new Phase[MAX_PHASES]; /** The id to be allocated for the next phase */ private static short nextPhaseId = 1; /** Run the phase globally. */ protected static final short SCHEDULE_GLOBAL = 1; /** Run the phase on collectors. */ protected static final short SCHEDULE_COLLECTOR = 2; /** Run the phase on mutators. */ protected static final short SCHEDULE_MUTATOR = 3; /** Run this phase concurrently with the mutators */ protected static final short SCHEDULE_CONCURRENT = 4; /** Don't run this phase. */ protected static final short SCHEDULE_PLACEHOLDER = 100; /** This is a complex phase. */ protected static final short SCHEDULE_COMPLEX = 101; /** * Retrieve a phase by the unique phase identifier. * * @param id The phase identifier. * @return The Phase instance. */ public static Phase getPhase(short id) { if (VM.VERIFY_ASSERTIONS) { VM.assertions._assert(id < nextPhaseId, "Phase ID unknown"); VM.assertions._assert(phases[id] != null, "Uninitialised phase"); } return phases[id]; } /** Get the phase id component of an encoded phase */ protected static short getPhaseId(int scheduledPhase) { short phaseId = (short) (scheduledPhase & 0x0000FFFF); if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(phaseId > 0); return phaseId; } /** * @param phaseId The unique phase identifier. * @return The name of the phase. */ public static String getName(short phaseId) { return phases[phaseId].name; } /** Get the ordering component of an encoded phase */ protected static short getSchedule(int scheduledPhase) { short ordering = (short) ((scheduledPhase >> 16) & 0x0000FFFF); if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(ordering > 0); return ordering; } /** Get the ordering component of an encoded phase */ protected static String getScheduleName(short ordering) { switch (ordering) { case SCHEDULE_GLOBAL: return "Global"; case SCHEDULE_COLLECTOR: return "Collector"; case SCHEDULE_MUTATOR: return "Mutator"; case SCHEDULE_CONCURRENT: return "Concurrent"; case SCHEDULE_PLACEHOLDER: return "Placeholder"; case SCHEDULE_COMPLEX: return "Complex"; default: return "UNKNOWN!"; } } /** * Construct a phase. * * @param name Display name of the phase */ @Interruptible public static short createSimple(String name) { return new SimplePhase(name).getId(); } /** * Construct a phase, re-using a specified timer. * * @param name Display name of the phase */ @Interruptible public static short createSimple(String name, Timer timer) { return new SimplePhase(name, timer).getId(); } /** * Construct a complex phase. * * @param name Display name of the phase * @param scheduledPhases The phases in this complex phase. */ @Interruptible public static short createComplex(String name, int... scheduledPhases) { return new ComplexPhase(name, scheduledPhases).getId(); } /** * Construct a complex phase, re-using a specified timer. * * @param name Display name of the phase * @param timer Timer for this phase to contribute to * @param scheduledPhases The phases in this complex phase. */ @Interruptible public static short createComplex(String name, Timer timer, int... scheduledPhases) { return new ComplexPhase(name, timer, scheduledPhases).getId(); } /** * Construct a phase. * * @param name Display name of the phase * @param atomicScheduledPhase The corresponding atomic phase to run in a stop the world * collection */ @Interruptible public static final short createConcurrent(String name, int atomicScheduledPhase) { return new ConcurrentPhase(name, atomicScheduledPhase).getId(); } /** * Construct a phase, re-using a specified timer. * * @param name Display name of the phase * @param timer Timer for this phase to contribute to * @param atomicScheduledPhase The corresponding atomic phase to run in a stop the world * collection */ @Interruptible public static final short createConcurrent(String name, Timer timer, int atomicScheduledPhase) { return new ConcurrentPhase(name, timer, atomicScheduledPhase).getId(); } /** * Take the passed phase and return an encoded phase to run that phase as a complex phase. * * @param phaseId The phase to run as complex * @return The encoded phase value. */ public static int scheduleComplex(short phaseId) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(Phase.getPhase(phaseId) instanceof ComplexPhase); return (SCHEDULE_COMPLEX << 16) + phaseId; } /** * Take the passed phase and return an encoded phase to run that phase as a concurrent phase. * * @param phaseId The phase to run as concurrent * @return The encoded phase value. */ public static int scheduleConcurrent(short phaseId) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(Phase.getPhase(phaseId) instanceof ConcurrentPhase); return (SCHEDULE_CONCURRENT << 16) + phaseId; } /** * Take the passed phase and return an encoded phase to run that phase in a global context; * * @param phaseId The phase to run globally * @return The encoded phase value. */ public static int scheduleGlobal(short phaseId) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(Phase.getPhase(phaseId) instanceof SimplePhase); return (SCHEDULE_GLOBAL << 16) + phaseId; } /** * Take the passed phase and return an encoded phase to run that phase in a collector context; * * @param phaseId The phase to run on collectors * @return The encoded phase value. */ public static int scheduleCollector(short phaseId) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(Phase.getPhase(phaseId) instanceof SimplePhase); return (SCHEDULE_COLLECTOR << 16) + phaseId; } /** * Take the passed phase and return an encoded phase to run that phase in a mutator context; * * @param phaseId The phase to run on mutators * @return The encoded phase value. */ public static int scheduleMutator(short phaseId) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(Phase.getPhase(phaseId) instanceof SimplePhase); return (SCHEDULE_MUTATOR << 16) + phaseId; } /** * Take the passed phase and return an encoded phase to run that phase in a mutator context; * * @param phaseId The phase to run on mutators * @return The encoded phase value. */ public static int schedulePlaceholder(short phaseId) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(Phase.getPhase(phaseId) instanceof SimplePhase); return (SCHEDULE_PLACEHOLDER << 16) + phaseId; } /** * ********************************************************************* * * <p>Phase instance fields/methods. */ /** The unique phase identifier. */ protected final short id; /** The name of the phase. */ protected final String name; /** The Timer that is started and stopped around the execution of this phase. */ protected final Timer timer; /** * Create a new Phase. This involves creating a corresponding Timer instance, allocating a unique * identifier, and registering the Phase. * * @param name The name for the phase. */ protected Phase(String name) { this(name, new Timer(name, false, true)); } /** * Create a new phase. This involves setting the corresponding Timer instance, allocating a unique * identifier, and registering the Phase. * * @param name The name of the phase. * @param timer The timer, or null if this is an untimed phase. */ protected Phase(String name, Timer timer) { this.name = name; this.timer = timer; this.id = nextPhaseId++; phases[this.id] = this; } /** @return The unique identifier for this phase. */ public final short getId() { return this.id; } /** Display a phase for debugging purposes. */ protected abstract void logPhase(); /** * ********************************************************************* * * <p>Phase stack */ /** The maximum stack depth for the phase stack. */ private static final int MAX_PHASE_STACK_DEPTH = MAX_PHASES; /** * Stores the current sub phase for a complex phase. Each entry corresponds to a phase stack entry */ private static int[] complexPhaseCursor = new int[MAX_PHASE_STACK_DEPTH]; /** The phase stack. Stores the current nesting of phases */ private static int[] phaseStack = new int[MAX_PHASE_STACK_DEPTH]; /** The current stack pointer */ private static int phaseStackPointer = -1; /** * The current even (0 mod 2) scheduled phase. As we only sync at the end of a phase we need this * to ensure that the primary thread setting the phase does not race with the other threads * reading it. */ private static int evenScheduledPhase; /** * The current odd (1 mod 2) scheduled phase. As we only sync at the end of a phase we need this * to ensure that the primary thread setting the phase does not race with the other threads * reading it. */ private static int oddScheduledPhase; /** * Do we need to add a sync point to reset the mutator count. This is necessary for consecutive * mutator phases and unneccessary otherwise. Again we separate in even and odd to ensure that * there is no race between the primary thread setting and the helper threads reading. */ private static boolean evenMutatorResetRendezvous; /** * Do we need to add a sync point to reset the mutator count. This is necessary for consecutive * mutator phases and unneccessary otherwise. Again we separate in even and odd to ensure that * there is no race between the primary thread setting and the helper threads reading. */ private static boolean oddMutatorResetRendezvous; /** * The complex phase whose timer should be started after the next rendezvous. We can not start the * timer at the point we determine the next complex phase as we determine the next phase at the * end of the previous phase before the sync point. */ private static short startComplexTimer; /** * The complex phase whose timer should be stopped after the next rendezvous. We can not start the * timer at the point we determine the next complex phase as we determine the next phase at the * end of the previous phase before the sync point. */ private static short stopComplexTimer; /** * Place a phase on the phase stack and begin processing. * * @param scheduledPhase The phase to execute * @return True if the phase stack is exhausted. */ public static boolean beginNewPhaseStack(int scheduledPhase) { int order = VM.collection.rendezvous(1001); if (order == 1) { pushScheduledPhase(scheduledPhase); } return processPhaseStack(false); } /** * Continue the execution of a phase stack. Used for incremental and concurrent collection. * * @return True if the phase stack is exhausted. */ public static boolean continuePhaseStack() { if (concurrentPhaseActive() && Plan.collectionTrigger != Collection.INTERNAL_PHASE_GC_TRIGGER) { resetConcurrentWorkers(); } return processPhaseStack(true); } /** Process the phase stack. This method is called by multiple threads. */ private static boolean processPhaseStack(boolean resume) { int order = VM.collection.rendezvous(1001); final boolean primary = order == 1; boolean log = Options.verbose.getValue() >= 6; boolean logDetails = Options.verbose.getValue() >= 7; if (primary && resume) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!Phase.isPhaseStackEmpty()); if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!Plan.gcInProgress()); Plan.setGCStatus(Plan.GC_PROPER); } /* In order to reduce the need for synchronization, we keep an odd or even * counter for the number of phases processed. As each phase has a single * rendezvous it is only possible to be out by one so the odd or even counter * protects us. */ boolean isEvenPhase = true; if (primary) { /* First phase will be even, so we say we are odd here so that the next phase set is even*/ setNextPhase(false, getNextPhase(), false); } /* Make sure everyone sees the first phase */ VM.collection.rendezvous(1002); /* Global and Collector instances used in phases */ Plan plan = VM.activePlan.global(); CollectorContext collector = VM.activePlan.collector(); /* The main phase execution loop */ int scheduledPhase; while ((scheduledPhase = getCurrentPhase(isEvenPhase)) > 0) { short schedule = getSchedule(scheduledPhase); short phaseId = getPhaseId(scheduledPhase); Phase p = getPhase(phaseId); /* Start the timer(s) */ if (primary) { if (resume) { resumeComplexTimers(); } if (p.timer != null) p.timer.start(); if (startComplexTimer > 0) { Phase.getPhase(startComplexTimer).timer.start(); startComplexTimer = 0; } } if (log) { Log.write("Execute "); p.logPhase(); } /* Execute a single simple scheduled phase */ switch (schedule) { /* Global phase */ case SCHEDULE_GLOBAL: { if (logDetails) Log.writeln(" as Global..."); if (primary) plan.collectionPhase(phaseId); break; } /* Collector phase */ case SCHEDULE_COLLECTOR: { if (logDetails) Log.writeln(" as Collector..."); collector.collectionPhase(phaseId, primary); break; } /* Mutator phase */ case SCHEDULE_MUTATOR: { if (logDetails) Log.writeln(" as Mutator..."); /* Iterate through all mutator contexts */ MutatorContext mutator; while ((mutator = VM.activePlan.getNextMutator()) != null) { mutator.collectionPhase(phaseId, primary); } break; } /* Concurrent phase */ case SCHEDULE_CONCURRENT: { /* We are yielding to a concurrent collection phase */ if (logDetails) Log.writeln(" as Concurrent, yielding..."); if (primary) { concurrentPhaseId = phaseId; scheduleConcurrentWorkers(); /* Concurrent phase, we need to stop gc */ Plan.setGCStatus(Plan.NOT_IN_GC); } VM.collection.rendezvous(1003); if (primary) { pauseComplexTimers(); } return false; } default: { /* getNextPhase has done the wrong thing */ VM.assertions.fail("Invalid schedule in Phase.processPhaseStack"); break; } } if (primary) { /* Set the next phase by processing the stack */ int next = getNextPhase(); boolean needsResetRendezvous = (next > 0) && (schedule == SCHEDULE_MUTATOR && getSchedule(next) == SCHEDULE_MUTATOR); setNextPhase(isEvenPhase, next, needsResetRendezvous); } /* Sync point after execution of a phase */ VM.collection.rendezvous(1004); /* Mutator phase reset */ if (primary && schedule == SCHEDULE_MUTATOR) { VM.activePlan.resetMutatorIterator(); } /* At this point, in the case of consecutive phases with mutator * scheduling, we have to double-synchronize to ensure all * collector threads see the reset mutator counter. */ if (needsMutatorResetRendezvous(isEvenPhase)) { VM.collection.rendezvous(1005); } /* Stop the timer(s) */ if (primary) { if (p.timer != null) p.timer.stop(); if (stopComplexTimer > 0) { Phase.getPhase(stopComplexTimer).timer.stop(); stopComplexTimer = 0; } } /* Flip the even / odd phase sense */ isEvenPhase = !isEvenPhase; resume = false; } /* Phase stack exhausted so we return true */ return true; } /** Get the next phase. */ private static int getCurrentPhase(boolean isEvenPhase) { return isEvenPhase ? evenScheduledPhase : oddScheduledPhase; } /** Do we need a mutator reset rendezvous in this phase? */ private static boolean needsMutatorResetRendezvous(boolean isEvenPhase) { return isEvenPhase ? evenMutatorResetRendezvous : oddMutatorResetRendezvous; } /** Set the next phase. If we are in an even phase the next phase is odd. */ private static void setNextPhase( boolean isEvenPhase, int scheduledPhase, boolean needsResetRendezvous) { if (isEvenPhase) { oddScheduledPhase = scheduledPhase; evenMutatorResetRendezvous = needsResetRendezvous; } else { evenScheduledPhase = scheduledPhase; oddMutatorResetRendezvous = needsResetRendezvous; } } /** * Pull the next scheduled phase off the stack. This may involve processing several complex phases * and skipping placeholders, etc. * * @return The next phase to run, or -1 if no phases are left. */ private static int getNextPhase() { boolean allowConcurrentPhase = Plan.collectionTrigger == Collection.INTERNAL_PHASE_GC_TRIGGER; while (phaseStackPointer >= 0) { int scheduledPhase = peekScheduledPhase(); short schedule = getSchedule(scheduledPhase); short phaseId = getPhaseId(scheduledPhase); switch (schedule) { case SCHEDULE_PLACEHOLDER: { /* Placeholders are ignored and we continue looking */ popScheduledPhase(); continue; } case SCHEDULE_GLOBAL: case SCHEDULE_COLLECTOR: case SCHEDULE_MUTATOR: { /* Simple phases are just popped off the stack and executed */ popScheduledPhase(); return scheduledPhase; } case SCHEDULE_CONCURRENT: { /* Concurrent phases are either popped off or we forward to * an associated non-concurrent phase. */ if (!allowConcurrentPhase) { popScheduledPhase(); ConcurrentPhase cp = (ConcurrentPhase) getPhase(phaseId); int alternateScheduledPhase = cp.getAtomicScheduledPhase(); if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(getSchedule(alternateScheduledPhase) != SCHEDULE_CONCURRENT); pushScheduledPhase(alternateScheduledPhase); continue; } if (VM.VERIFY_ASSERTIONS) { /* Concurrent phases can not have a timer */ VM.assertions._assert(getPhase(getPhaseId(scheduledPhase)).timer == null); } return scheduledPhase; } case SCHEDULE_COMPLEX: { /* A complex phase may either be a newly pushed complex phase, * or a complex phase we are in the process of executing in * which case we move to the next subphase. */ ComplexPhase p = (ComplexPhase) getPhase(phaseId); int cursor = incrementComplexPhaseCursor(); if (cursor == 0 && p.timer != null) { /* Tell the primary thread to start the timer after the next sync. */ startComplexTimer = phaseId; } if (cursor < p.count()) { /* There are more entries, we push the next one and continue */ pushScheduledPhase(p.get(cursor)); continue; } /* We have finished this complex phase */ popScheduledPhase(); if (p.timer != null) { /* Tell the primary thread to stop the timer after the next sync. */ stopComplexTimer = phaseId; } continue; } default: { VM.assertions.fail("Invalid phase type encountered"); } } } return -1; } /** Pause all of the timers for the complex phases sitting in the stack. */ private static void pauseComplexTimers() { for (int i = phaseStackPointer; i >= 0; i--) { Phase p = getPhase(getPhaseId(phaseStack[i])); if (p.timer != null) p.timer.stop(); } } /** Resume all of the timers for the complex phases sitting in the stack. */ private static void resumeComplexTimers() { for (int i = phaseStackPointer; i >= 0; i--) { Phase p = getPhase(getPhaseId(phaseStack[i])); if (p.timer != null) p.timer.start(); } } /** * Return true if phase stack is empty, false otherwise. * * @return true if phase stack is empty, false otherwise. */ @Inline public static boolean isPhaseStackEmpty() { return phaseStackPointer < 0; } /** Clears the scheduled phase stack. */ @Inline public static void resetPhaseStack() { phaseStackPointer = -1; } /** * Push a scheduled phase onto the top of the work stack. * * @param scheduledPhase The scheduled phase. */ @Inline public static void pushScheduledPhase(int scheduledPhase) { phaseStack[++phaseStackPointer] = scheduledPhase; complexPhaseCursor[phaseStackPointer] = 0; } /** * Increment the cursor associated with the current phase stack entry. This is used to remember * the current sub phase when executing a complex phase. * * @return The old value of the cursor. */ @Inline private static int incrementComplexPhaseCursor() { return complexPhaseCursor[phaseStackPointer]++; } /** Pop off the scheduled phase at the top of the work stack. */ @Inline private static int popScheduledPhase() { return phaseStack[phaseStackPointer--]; } /** Peek the scheduled phase at the top of the work stack. */ @Inline private static int peekScheduledPhase() { return phaseStack[phaseStackPointer]; } /** The concurrent phase being executed */ private static short concurrentPhaseId; /** An atomic counter used to determine the threads performing gc work. */ private static Lock concurrentWorkersLock = VM.newLock("Active Concurrent Workers"); /** The number of concurrent workers currently active */ private static int concurrentWorkersActive; /** Do we want to allow new concurrent workers to become active */ private static boolean allowConcurrentWorkersActive; /** Get the current phase Id. */ public static short getConcurrentPhaseId() { return concurrentPhaseId; } /** @return True if there is an active concurrent phase. */ public static boolean concurrentPhaseActive() { return (concurrentPhaseId > 0); } /** Attempt to begin execution of a concurrent collection phase. */ public static boolean startConcurrentPhase() { boolean result = false; concurrentWorkersLock.acquire(); if (concurrentPhaseActive()) { if (allowConcurrentWorkersActive) { concurrentWorkersActive++; result = true; } VM.activePlan.collector().clearResetConcurrentWork(); } if (Options.verbose.getValue() >= 2) { if (result) { Log.write("< Concurrent worker "); Log.write(concurrentWorkersActive - 1); Log.write(" started phase "); Log.write(getName(concurrentPhaseId)); Log.writeln(" >"); } else { Log.writeln("< worker failed in attempt to start phase >"); } } concurrentWorkersLock.release(); return result; } /** * Reset the workers that believe they are in the middle of a concurrent phase but have been * pre-empted by a collection. */ public static void resetConcurrentWorkers() { for (int i = 0; i < VM.activePlan.collectorCount(); i++) { VM.activePlan.collector(i).resetConcurrentWork(); } concurrentWorkersActive = 0; concurrentPhaseId = 0; allowConcurrentWorkersActive = false; } /** * Notify that the current thread believes that a concurrent collection phase is complete. * * @return True if this was the last thread. */ public static boolean completeConcurrentPhase() { boolean result = false; concurrentWorkersLock.acquire(); if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(allowConcurrentWorkersActive); concurrentWorkersActive--; if (concurrentWorkersActive == 0) { allowConcurrentWorkersActive = false; result = true; } if (Options.verbose.getValue() >= 3) { Log.write("< Concurrent worker "); Log.write(concurrentWorkersActive); Log.write(" completed phase "); Log.write(getName(concurrentPhaseId)); Log.writeln(" >"); } concurrentWorkersLock.release(); return result; } /** * Notify that the concurrent phase has completed successfully. This must only be called by a * single thread after it has determined that the phase has been completed successfully. */ public static void notifyConcurrentPhaseComplete() { if (Options.verbose.getValue() >= 2) { Log.write("< Concurrent phase "); Log.write(getName(concurrentPhaseId)); Log.writeln(" complete >"); } /* Concurrent phase is complete*/ concurrentPhaseId = 0; /* Remove it from the stack */ popScheduledPhase(); /* Pop the next phase off the stack */ int nextScheduledPhase = getNextPhase(); if (nextScheduledPhase > 0) { short schedule = getSchedule(nextScheduledPhase); /* A concurrent phase, lets wake up and do it all again */ if (schedule == SCHEDULE_CONCURRENT) { concurrentPhaseId = getPhaseId(nextScheduledPhase); scheduleConcurrentWorkers(); return; } /* Push phase back on and resume atomic collection */ pushScheduledPhase(nextScheduledPhase); VM.collection.triggerAsyncCollection(Collection.INTERNAL_PHASE_GC_TRIGGER); } } /** Notify that the concurrent phase has not finished, and must be re-attempted. */ public static void notifyConcurrentPhaseIncomplete() { if (Options.verbose.getValue() >= 2) { Log.write("< Concurrent phase "); Log.write(getName(concurrentPhaseId)); Log.writeln(" incomplete >"); } scheduleConcurrentWorkers(); } /** * Schedule the concurrent collector threads. If this is called from within a collection interval * the threads must not be started until after the interval is complete. */ private static void scheduleConcurrentWorkers() { concurrentWorkersLock.acquire(); if (!allowConcurrentWorkersActive) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(concurrentWorkersActive == 0); allowConcurrentWorkersActive = true; } concurrentWorkersLock.release(); VM.collection.scheduleConcurrentWorkers(); } }
/** * ************************************************************************** * * <p>Initialization */ static { classLock = VM.newLock("PageResource"); Options.protectOnRelease = new ProtectOnRelease(); }