Skip to end of metadata
Go to start of metadata

We will now modify the Tutorial collector to perform allocation and collection according to a mark-sweep policy. First we will change the allocation from bump-allocation to free-list allocation (but still no collector whatsoever), and then we will add a mark-sweep collection policy, yielding a complete mark-sweep collector.

Free-list Allocation

This step will change your simple collector from using a bump pointer to a free list (but still without any garbage collection).

  1. Update the constraints for this collector to reflect the constraints of a mark-sweep system, by updating TutorialConstraintsas follows:
    • gcHeaderBits() should return MarkSweepSpace.LOCAL_GC_BITS_REQUIRED.
    • gcHeaderWords() should return MarkSweepSpace.GC_HEADER_WORDS_REQUIRED.
    • requiresLOS() should return true (because the free list cannot accommodate large objects).
  2. In Tutorial, replace the ImmortalSpace with a MarkSweepSpace:
    • rename the variable defSpace to msSpace (right-click, Refactor→Rename...)
    • rename the variable DEF to MARK_SWEEP (right-click, Refactor→Rename...)
    • change the type and static initialization of msSpace appropriately (MarkSweepSpace msSpace = new MarkSweepSpace("ms", DEFAULT_POLL_FREQUENCY, VMRequest.create())).
    • add an import for MarkSweepSpace and remove the redundant import for ImmortalSpace.
  3. In TutorialMutator, replace the ImmortalLocal (a bump pointer) with a
    MarkSweepLocal(a free-list allocator)
    • rename the variable def to ms (right-click, Refactor→Rename...)
    • change the type of ms and change the static initializer appropriately.
    • change the appropriate import statement from ImmortalLocal to MarkSweepLocal.
  4. Fix postAlloc()to initialize the mark-sweep header:

        if (allocator == Tutorial.ALLOC_DEFAULT) {
          Tutorial.msSpace.postAlloc(ref);
        } else {
          super.postAlloc(ref, typeRef, bytes, allocator);
        }

With these changes, Tutorial should now work, just as it did before, only
exercising a free list (mark-sweep) allocator rather than a bump pointer
(immortal) allocator. Create a BaseBaseTutorial build, and test your system
to ensure it performs just as it did before. You may notice that its memory
is exhausted slightly earlier because the free list allocator is slightly less
efficient in space utilization than the bump pointer allocator.

Checkpoint

Icon

This patch captures all of the above steps with respect to Jikes RVM 3.0.1. You can use the patch to verify you've completed the above steps correctly.

Mark-sweep Collection.

The next change required is to perform mark-and-sweep collection whenever
the heap is exhausted. The poll() method of a plan is called at appropriate
intervals by other MMTk components to ask the plan whether a collection
is required.

  1. Change TutorialConstraintsso that it inherits constraints from a collecting plan:

      public class TutorialConstraints extends StopTheWorldConstraints
  2. The plan needs to know how to perform a garbage collection. Collections are performed in phases, coordinated by data structures defined in StopTheWorld, and have global and thread-local components. First ensure the global components are behaving correctly. These are defined in Tutorial (which is implicitly global).
    • Make Tutorial extend StopTheWorld (for stop-the-world garbage collection) rather than Plan (the superclass of StopTheWorld: public class Tutorial extends StopTheWorld
    • Rename the trace variable to mstrace (right-click, Refactor→Rename...)
    • Add code to ensure that Tutorial performs the correct global collection phases in collectionPhase():
      • First remove the assertion that the code is never called (if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(false);).
      • Add the prepare phase, preparing both the global tracer (msTrace) and the space (msSpace), after first performing the preparation phases associated with the superclasses. Using the commented template in Tutorial.collectionPhase(), set the following within the clause for phaseId == PREPARE:

            if (phaseId == PREPARE) {
              super.collectionPhase(phaseId);
              msTrace.prepare();
              msSpace.prepare(true);
              return;
            }
      • Add the closure phase, again preparing the global tracer (msTrace):

            if (phaseId == CLOSURE) {
              msTrace.prepare();
              return;
            }
      • Add the release phase, releasing the global tracer (msTrace) and the space (msSpace) before performing the release phases associated with the superclass:

            if (phaseId == RELEASE) {
              msTrace.release();
              msSpace.release();
              super.collectionPhase(phaseId);
              return;
            }
      • Finally ensure that for all other cases, the phases are delegated to the superclass, uncommenting the following after all of the above conditionals:

            super.collectionPhase(phaseId);
    • Add a new accounting method that determines how much space a collection needs to yield to the mutator. The method, getPagesRequired, overrides the one provided in the StopTheWorldsuperclass:

        @Override
        public int getPagesRequired() {
          return super.getPagesRequired() + msSpace.requiredPages();
        }
    • Add a new method that determines whether an object will move during collection:

        @Override
        public boolean willNeverMove(ObjectReference object) {
          if (Space.isInSpace(MARK_SWEEP, object))
            return true;
          return super.willNeverMove(object);
        }
    • Remove the method collectionRequired(), falling back on the superclass, StopTheWorld.
  1. Next ensure that Tutorial correctly performs local collection phases. These are defined in TutorialCollector.
    • Make TutorialCollector extend StopTheWorldCollector:
      • Extend the class (public class TutorialCollector extends StopTheWorldCollector).
      • Import StopTheWorldCollector.
      • Remove some methods now implemented by StopTheWorldCollector: collect(), concurrentCollect(), and concurrentCollectionPhase().
    • Add code to ensure that TutorialCollector performs the correct global collection phases in collectionPhase():
      • First remove the assertion that the code is never called (if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(false);).
      • Add the prepare phase, preparing the local tracer (trace) after first performing the preparation phases associated with the superclasses. Using the commented template in Tutorial.collectionPhase(), set the following within the clause for phaseId == PREPARE:

            if (phaseId == Tutorial.PREPARE) {
              super.collectionPhase(phaseId, primary);
              trace.prepare();
              return;
            }
      • Add the closure phase, again preparing the local tracer (trace):

            if (phaseId == Tutorial.CLOSURE) {
              trace.completeTrace();
              return;
            }
      • Add the release phase, releasing the local tracer (trace) before performing the release phases associated with the superclass:

            if (phaseId == Tutorial.RELEASE) {
              trace.release();
              super.collectionPhase(phaseId, primary);
              return;
            }
      • Finally ensure that for all other cases, the phases are delegated to the superclass, uncommenting the following after all of the above conditionals:

            super.collectionPhase(phaseId, primary);
  2. Finally ensure that Tutorial correctly performs local mutator-related collection activities:
    • Make TutorialMutator extend StopTheWorldMutator:
      • Extend the class: public class TutorialMutator extends StopTheWorldMutator.
      • Import StopTheWorldMutator.
    • Update the mutator-side collection phases:
      • Add the prepare phase to collectionPhase() which prepares mutator-side data structures(namely the per-thread free lists) for the startof a collection:

            if (phaseId == MS.PREPARE) {
              super.collectionPhase(phaseId, primary);
              ms.prepare();
              return;
            }
      • Add the release phase to collectionPhase() which re-initializes mutator-side data structures (namely the per-thread free lists) after the endof a collection:

            if (phaseId == MS.RELEASE) {
              ms.release();
              super.collectionPhase(phaseId, primary);
              return;
            }
      • Finally, delegate all other phases to the superclass:

            super.collectionPhase(phaseId, primary);

With these changes, Tutorial should now work with both mark-sweep allocation and collection. Create a BaseBaseTutorial build, and test your system to ensure it performs just as it did before. You can observe the effect of garbage collection as the program runs by adding -X:gc:verbose=1 to your command line as the first argument after rvm. If you run a very simple program (such as HelloWorld), you might not observe any garbage collection. In that case, try running a larger program such as a DaCapo benchmark. You may also observe that the output from -X:gc:verbose=1 indicates that the heap is growing. Dynamic heap resizing is normal default behavior for a JVM. You can override this by providing minimum (-Xms) and maximum (-Xmx) heap sizes (these are standard arguments respected by all JVMs. The heap size should be specified in bytes as an integer and a unit (K, M, G), for example: -Xms20M -Xmx20M.

Checkpoint

Icon

This patch captures all of the above steps with respect to Jikes RVM 3.0.1. You can use the patch to verify you've completed the above steps correctly.

Optimized Mark-sweep Collection.

MMTk has a unique capacity to allow specialization of the performance-critical scanning loop. This is particularly valuable in collectors which have more than one mode of collection (such as in a generational collector), so each of the collection paths is explicitly specialized at build time, removing conditionals from the hot portion of the tracing loop at the core of the collector. Enabling this involves just two small steps:

  1. Indicate the number of specialized scanning loops and give each a symbolic name, which at this stage is just one since we have a very simple collector:
    • Override the numSpecializedScans() getter method in TutorialConstraints:

        public int numSpecializedScans() { return 1; }
    • Define a constant to represent our (only) specialized scan in Tutorial(we will call this scan "mark"):

        public static final int SCAN_MARK = 0;
  2. Register the specialized method by adding the following line to registerSpecializedMethods() method in Tutorial.:

        TransitiveClosure.registerSpecializedScan(SCAN_MARK, TutorialTraceLocal.class);

Checkpoint

Icon

This patch captures all of the above steps with respect to Jikes RVM 3.0.1. You can use the patch to verify you've completed the above steps correctly.

  • No labels