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).
- Update the constraints for this collector to reflect the constraints of a mark-sweep system, by updating
TutorialConstraints
as follows:gcHeaderBits()
should returnMarkSweepSpace.LOCAL_GC_BITS_REQUIRED
.gcHeaderWords()
should returnMarkSweepSpace.GC_HEADER_WORDS_REQUIRED
.requiresLOS()
should returntrue
(because the free list cannot accommodate large objects).
- In
Tutorial
, replace theImmortalSpace
with aMarkSweepSpace
:- rename the variable
defSpace
tomsSpace
(right-click, Refactor→Rename...) - rename the variable
DEF
toMARK_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 forImmortalSpace
.
- rename the variable
- In
TutorialMutator
, replace theImmortalLocal
(a bump pointer) with a
MarkSweepLocal
(a free-list allocator)- rename the variable
def
toms
(right-click, Refactor→Rename...) - change the type of
ms
and change the static initializer appropriately. - change the appropriate import statement from
ImmortalLocal
toMarkSweepLocal
.
- rename the variable
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 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.
Change
TutorialConstraints
so that it inherits constraints from a collecting plan:public class TutorialConstraints extends StopTheWorldConstraints
- 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 inTutorial
(which is implicitly global).- Make
Tutorial
extendStopTheWorld
(for stop-the-world garbage collection) rather thanPlan
(the superclass ofStopTheWorld
:public class Tutorial extends StopTheWorld
- Rename the
trace
variable tomstrace
(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 inTutorial.collectionPhase()
, set the following within the clause forphaseId == 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);
- First remove the assertion that the code is never called (
- Make
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 theStopTheWorld
superclass:@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
.
- Next ensure that Tutorial correctly performs local collection phases. These are defined in
TutorialCollector
.- Make
TutorialCollector
extendStopTheWorldCollector
:- Extend the class (
public class TutorialCollector extends StopTheWorldCollector
). - Import
StopTheWorldCollector
. - Remove some methods now implemented by
StopTheWorldCollector
:collect()
,concurrentCollect()
, andconcurrentCollectionPhase()
.
- Extend the class (
- Add code to ensure that
TutorialCollector
performs the correct global collection phases incollectionPhase()
:- 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 inTutorial.collectionPhase()
, set the following within the clause forphaseId == 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);
- First remove the assertion that the code is never called (
- Make
- Finally ensure that Tutorial correctly performs local mutator-related collection activities:
- Make
TutorialMutator
extendStopTheWorldMutator
:- Extend the class:
public class TutorialMutator extends StopTheWorldMutator
. - Import
StopTheWorldMutator
.
- Extend the class:
- 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);
- Make
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 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:
- 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 inTutorialConstraints
: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;
Register the specialized method by adding the following line to
registerSpecializedMethods()
method inTutorial
.:TransitiveClosure.registerSpecializedScan(SCAN_MARK, TutorialTraceLocal.class);
![]() | Checkpoint 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. |