|MMTk phases and the flow|
build/configs folder contains multiple properties files, which can be used to build Jikes RVM with different configurations and different collectors. MMTk plan is defined in the properties files. The recommended production or prototype configurations use GenImmix plan, as can be seen from production.properties and prototype.properties.
Plan is the abstract class that defines the core functionalities of all the memory management schemes, and hence becomes the base class for all the collector plans.
Global and local states are separated into different classes, as in the case of policies, as plans make a clear distinction between the global and thread-local activities. Global activities should be synchronized, where this is not required for thread-local activities. Static resources such as VM and memory resources are defined and managed by the global instances.
ParallelCollectorGroup is a pool of collector contexts, that can be triggered to perform collection. Plan consists two instances of this, named parallelWorkers and concurrentWorkers.
The respective sub class of Plan will have a single instance and there will be a one-to-one mapping between the PlanLocal and CPUs (kernel threads). This mapping is crucial in understanding the properties of the plans. Due to this separation, instance methods of the Local let the functions such as allocation and collection unsynchronized and faster.
2. Boot Sequence
The boot sequence calls the methods given below in the given order.
1. enableAllocation() - called early in the boot process for the allocation.
2. processOptions() - called immediately by the run time, as the command line arguments are available. Calling the method enableAllocation() is a precondition, as the infrastructure may require allocation to be able to parse the arguments. All plans operate in the default minimum, till this method is called, and the respective values are obtained.
3. enableCollection() - called when it is safe to spawn contexts and start garbage collection. If the threads have not explicitly been set, right defaults will be used. Creates the parallel workers and concurrent workers. Creates the control thread and allows mutators to trigger the collection.
4. fullyBooted() - called just before giving the control back to the application, and hence marks the completion of the collection cycle.
Before the VM exists, notifyExit() is called for the clean up. Further, plan specific timing information can be provided by overriding printDetailedTiming(). collectionPhase() performs a global collection phase.
Nursery refers to the collection of objects allocated since last GC, hence a collection of short living objects. Generational collectors function based on the observation that an object living longer than a specific amount of time will live further long. That means, when a few objects in the nursery survives a few round of collections in the nursery, they are moved to the matured space. Matured space consists of 90% of space, but of 10% of the number of objects that are created from the beginning. Nursery contains just 10% of the heap space, but contains 90% of the objects created ever since, as the objects in nursery are often short-lived. Matured space is scanned rarer than the nursery in the generational collector algorithms, where the matured space is scanned only when the specific time occurs, or when the VM runs out of memory. IsCurrentGCNursery() checks whether the current GC is only collecting the objects allocated after the last garbage collection.
After a full heap GC, sanityLinearScan() performs a linear scan of all spaces for possible leaks. Triggering of a garbage collection is controlled by collectionRequired(). This is called periodically during the allocation. If collection is requested by the plan, this method would return true. Whether the object can ever move is determined by the method willNeverMove().
MutatorContext and its subclasses define the per-mutator thread behaviour. MutatorContext is the base class of all the per-mutator contexts, ConcurrentMutator, StopTheWorldMutator, and all the XXXMutator (GC-specific sub classes), where XXX refers to the names of the collector plans such as GenImmix or CMS.
This implements the basic unsynchronized per-mutator behaviour that is common to all the collectors such as the support for immortal and large object space allocation and empty stubs for write barriers.
initMutator() is called before the MutatorContext is used. It is called after the context is fully registered and visible to the collection. It notifies that the context is registered such that it will be included in the iterations over the mutators. Similarly, deinitMutator() is the final method to be called, where the mutator is to be cleaned, and hence all local data should be returned. The implementation of deinitMutator() in this class, as well as the sub classes of MutatorContext by default, call the flush(), which flushes the mutator context, flushing the remembered sets into the global remset pool.
checkAllocator() performs a run time check for the allocator.
CollectorContext is an abstract class that is extended by the classes XXXCollector, similar to the Mutators. These classes implement the per-collector thread behaviour and structures such as collection work queues.
MMTk assumes that for each thread participating in the collection, the VM instantiates instances of CollectorContext and MutatorContext in thread local storage (TLS). Hence the access to this state is assumed to be low-cost at GC time (opposed to the access to MutatorContext, which is low-cost during mutator time), essentially separating this class which is thread-local, from the global operations in the Plan class. Operations in this class and the subclasses are local to each collector thread, where operations in the MutatorContext class and all of its sub classes are local to each mutator thread, whilst the synchronized operations are via the access to Plan and its subclasses, which are global. Synchronization is localized, explicit, and thus minimized.
Memory is allocated for an object using alloc(). Any required post allocation operations are handled by postAlloc(). getAllocatorFromSpace() finds the allocator that is associated with the given space.
The number of active collector threads (n), which is often the number of processors and the number of mutator (application) threads (m) are decided by the VM, and not MMTk. Collector operations are separated as below.
Per-collector thread operations: The operations for the entire GC, performed by each collector thread, and m per-mutator thread operations multiplexed across the n active collector threads.
Per-mutator thread operations: The operations such as flushing and restoring per-mutator state.
The unpreemptible method run() provides an entry point for the collector context.
Space is allocated for copying the space using allocCopy(). This method just allocates the space, and doesn't really copy the object. Actions after the copy are performed by postCopy(). Run time check of the allocator for the copy allocation is performed by copyCheckAllocator().
We often need to find the number of parallel workers currently executing to be able to load balance better. This can be found by calling parallelWorkerCount(), from anywhere within the collector context.
This simple No-garbage-collector class implements the global state of a simple allocator without a collector.
The abstract class Simple provides the core functionalities of a Simple collector, which should be extended by the two types of collectors, non-concurrent or concurrent. Collection phases are defined along with their base level implementations. The spaces introduced by the subclasses should be implemented by them respectively.
All the non-concurrent collectors should extend the abstract class StopTheWorld.
The abstract class Concurrent implements the global state of a concurrent collector.