pub trait Scanning<VM: VMBinding> {
// Required methods
fn scan_object<SV: SlotVisitor<VM::VMSlot>>(
tls: VMWorkerThread,
object: ObjectReference,
slot_visitor: &mut SV
);
fn notify_initial_thread_scan_complete(
partial_scan: bool,
tls: VMWorkerThread
);
fn scan_roots_in_mutator_thread(
tls: VMWorkerThread,
mutator: &'static mut Mutator<VM>,
factory: impl RootsWorkFactory<VM::VMSlot>
);
fn scan_vm_specific_roots(
tls: VMWorkerThread,
factory: impl RootsWorkFactory<VM::VMSlot>
);
fn supports_return_barrier() -> bool;
fn prepare_for_roots_re_scanning();
// Provided methods
fn support_slot_enqueuing(
_tls: VMWorkerThread,
_object: ObjectReference
) -> bool { ... }
fn scan_object_and_trace_edges<OT: ObjectTracer>(
_tls: VMWorkerThread,
_object: ObjectReference,
_object_tracer: &mut OT
) { ... }
fn process_weak_refs(
_worker: &mut GCWorker<VM>,
_tracer_context: impl ObjectTracerContext<VM>
) -> bool { ... }
fn forward_weak_refs(
_worker: &mut GCWorker<VM>,
_tracer_context: impl ObjectTracerContext<VM>
) { ... }
}
Expand description
VM-specific methods for scanning roots/objects.
Required Methods§
sourcefn scan_object<SV: SlotVisitor<VM::VMSlot>>(
tls: VMWorkerThread,
object: ObjectReference,
slot_visitor: &mut SV
)
fn scan_object<SV: SlotVisitor<VM::VMSlot>>( tls: VMWorkerThread, object: ObjectReference, slot_visitor: &mut SV )
Delegated scanning of a object, visiting each reference field encountered.
The VM shall call slot_visitor.visit_slot
on each reference field. This effectively
visits all outgoing edges from the current object in the form of slots.
The VM may skip a reference field if it is not holding an object reference (e.g. if the
field is holding a null reference, or a tagged non-reference value such as small integer).
Even if not skipped, Slot::load
will still return None
if the slot is not holding an
object reference.
The memory_manager::is_mmtk_object
function can be used in this function if
- the “is_mmtk_object” feature is enabled, and
VM::VMObjectModel::NEED_VO_BITS_DURING_TRACING
is true.
Arguments:
tls
: The VM-specific thread-local storage for the current worker.object
: The object to be scanned.slot_visitor
: Called back for each field.
sourcefn notify_initial_thread_scan_complete(partial_scan: bool, tls: VMWorkerThread)
fn notify_initial_thread_scan_complete(partial_scan: bool, tls: VMWorkerThread)
MMTk calls this method at the first time during a collection that thread’s stacks have been scanned. This can be used (for example) to clean up obsolete compiled methods that are no longer being executed.
Arguments:
partial_scan
: Whether the scan was partial or full-heap.tls
: The GC thread that is performing the thread scan.
sourcefn scan_roots_in_mutator_thread(
tls: VMWorkerThread,
mutator: &'static mut Mutator<VM>,
factory: impl RootsWorkFactory<VM::VMSlot>
)
fn scan_roots_in_mutator_thread( tls: VMWorkerThread, mutator: &'static mut Mutator<VM>, factory: impl RootsWorkFactory<VM::VMSlot> )
Scan one mutator for stack roots.
Some VM bindings may not be able to implement this method.
For example, the VM binding may only be able to enumerate all threads and
scan them while enumerating, but cannot scan stacks individually when given
the references of threads.
In that case, it can leave this method empty, and deal with stack
roots in Scanning::scan_vm_specific_roots
. However, in that case, MMTk
does not know those roots are stack roots, and cannot perform any possible
optimization for the stack roots.
The memory_manager::is_mmtk_object
function can be used in this function if
- the “is_mmtk_object” feature is enabled.
Arguments:
tls
: The GC thread that is performing this scanning.mutator
: The reference to the mutator whose roots will be scanned.factory
: The VM uses it to create work packets for scanning roots.
sourcefn scan_vm_specific_roots(
tls: VMWorkerThread,
factory: impl RootsWorkFactory<VM::VMSlot>
)
fn scan_vm_specific_roots( tls: VMWorkerThread, factory: impl RootsWorkFactory<VM::VMSlot> )
Scan VM-specific roots. The creation of all root scan tasks (except thread scanning) goes here.
The memory_manager::is_mmtk_object
function can be used in this function if
- the “is_mmtk_object” feature is enabled.
Arguments:
tls
: The GC thread that is performing this scanning.factory
: The VM uses it to create work packets for scanning roots.
sourcefn supports_return_barrier() -> bool
fn supports_return_barrier() -> bool
Return whether the VM supports return barriers. This is unused at the moment.
sourcefn prepare_for_roots_re_scanning()
fn prepare_for_roots_re_scanning()
Prepare for another round of root scanning in the same GC. Some GC algorithms
need multiple transitive closures, and each transitive closure starts from
root scanning. We expect the binding to provide the same root set for every
round of root scanning in the same GC. Bindings can use this call to get
ready for another round of root scanning to make sure that the same root
set will be returned in the upcoming calls of root scanning methods,
such as crate::vm::Scanning::scan_roots_in_mutator_thread
and
crate::vm::Scanning::scan_vm_specific_roots
.
Provided Methods§
sourcefn support_slot_enqueuing(
_tls: VMWorkerThread,
_object: ObjectReference
) -> bool
fn support_slot_enqueuing( _tls: VMWorkerThread, _object: ObjectReference ) -> bool
Return true if the given object supports slot enqueuing.
- If this returns true, MMTk core will call
scan_object
on the object. - Otherwise, MMTk core will call
scan_object_and_trace_edges
on the object.
For maximum performance, the VM should support slot-enqueuing for as many objects as practical. Also note that this method is called for every object to be scanned, so it must be fast. The VM binding should avoid expensive checks and keep it as efficient as possible.
Arguments:
tls
: The VM-specific thread-local storage for the current worker.object
: The object to be scanned.
sourcefn scan_object_and_trace_edges<OT: ObjectTracer>(
_tls: VMWorkerThread,
_object: ObjectReference,
_object_tracer: &mut OT
)
fn scan_object_and_trace_edges<OT: ObjectTracer>( _tls: VMWorkerThread, _object: ObjectReference, _object_tracer: &mut OT )
Delegated scanning of a object, visiting each reference field encountered, and tracing the objects pointed by each field.
The VM shall call object_tracer.trace_object
with the argument being the object reference
held in each reference field. If the GC moves the object, the VM shall update the field so
that it refers to the object using the object reference returned from trace_object
. This
effectively traces through all outgoing edges from the current object directly.
The VM must skip reference fields that are not holding object references (e.g. if the field is holding a null reference, or a tagged non-reference value such as small integer).
The memory_manager::is_mmtk_object
function can be used in this function if
- the “is_mmtk_object” feature is enabled, and
VM::VMObjectModel::NEED_VO_BITS_DURING_TRACING
is true.
Arguments:
tls
: The VM-specific thread-local storage for the current worker.object
: The object to be scanned.object_tracer
: Called back for the object reference held in each field.
sourcefn process_weak_refs(
_worker: &mut GCWorker<VM>,
_tracer_context: impl ObjectTracerContext<VM>
) -> bool
fn process_weak_refs( _worker: &mut GCWorker<VM>, _tracer_context: impl ObjectTracerContext<VM> ) -> bool
Process weak references.
This function is called after a transitive closure is completed.
MMTk core enables the VM binding to do the following in this function:
- Query if an object is already reached in this transitive closure.
- Get the new address of an object if it is already reached.
- Keep an object and its descendents alive if not yet reached.
- Request this function to be called again after transitive closure is finished again.
The VM binding can query if an object is currently reached by calling
ObjectReference::is_reachable()
.
If an object is already reached, the VM binding can get its new address by calling
ObjectReference::get_forwarded_object()
as the object may have been moved.
If an object is not yet reached, the VM binding can keep that object and its descendents
alive. To do this, the VM binding should use tracer_context.with_tracer
to get access to
an ObjectTracer
, and then call its trace_object(object)
method. The trace_object
method will return the new address of the object
if it moved the object, or its original
address if not moved. Implementation-wise, the ObjectTracer
may contain an internal
queue for newly traced objects, and will flush the queue when tracer_context.with_tracer
returns. Therefore, it is recommended to reuse the ObjectTracer
instance to trace
multiple objects.
Note that if trace_object
is called on an already reached object, the behavior will be
equivalent to ObjectReference::get_forwarded_object()
. It will return the new address if
the GC already moved the object when tracing that object, or the original address if the GC
did not move the object when tracing it. In theory, the VM binding can use trace_object
wherever ObjectReference::get_forwarded_object()
is needed. However, if a VM never
resurrects objects, it should completely avoid touching tracer_context
, and exclusively
use ObjectReference::get_forwarded_object()
to get new addresses of objects. By doing
so, the VM binding can avoid accidentally resurrecting objects.
The VM binding can return true
from process_weak_refs
to request process_weak_refs
to be called again after the MMTk core finishes transitive closure again from the objects
newly visited by ObjectTracer::trace_object
. This is useful if a VM supports multiple
levels of reachabilities (such as Java) or ephemerons.
Implementation-wise, this function is called as the “sentinel” of the VMRefClosure
work
bucket, which means it is called when all work packets in that bucket have finished. The
tracer_context
expands the transitive closure by adding more work packets in the same
bucket. This means if process_weak_refs
returns true, those work packets will have
finished (completing the transitive closure) by the time process_weak_refs
is called
again. The VM binding can make use of this by adding custom work packets into the
VMRefClosure
bucket. The bucket will be VMRefForwarding
, instead, when forwarding.
See below.
The memory_manager::is_mmtk_object
function can be used in this function if
- the “is_mmtk_object” feature is enabled, and
VM::VMObjectModel::NEED_VO_BITS_DURING_TRACING
is true.
Arguments:
worker
: The current GC worker.tracer_context
: Use this to get access anObjectTracer
and use it to retain and update weak references.
This function shall return true if this function needs to be called again after the GC finishes expanding the transitive closure from the objects kept alive.
sourcefn forward_weak_refs(
_worker: &mut GCWorker<VM>,
_tracer_context: impl ObjectTracerContext<VM>
)
fn forward_weak_refs( _worker: &mut GCWorker<VM>, _tracer_context: impl ObjectTracerContext<VM> )
Forward weak references.
This function will only be called in the forwarding stage when using the mark-compact GC algorithm. Mark-compact computes transive closure twice during each GC. It marks objects in the first transitive closure, and forward references in the second transitive closure.
Arguments:
worker
: The current GC worker.tracer_context
: Use this to get access anObjectTracer
and use it to update weak references.