pub trait Scanning<VM: VMBinding> {
// Required methods
fn scan_object<EV: EdgeVisitor<VM::VMEdge>>(
tls: VMWorkerThread,
object: ObjectReference,
edge_visitor: &mut EV
);
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::VMEdge>
);
fn scan_vm_specific_roots(
tls: VMWorkerThread,
factory: impl RootsWorkFactory<VM::VMEdge>
);
fn supports_return_barrier() -> bool;
fn prepare_for_roots_re_scanning();
// Provided methods
fn support_edge_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<EV: EdgeVisitor<VM::VMEdge>>(
tls: VMWorkerThread,
object: ObjectReference,
edge_visitor: &mut EV
)
fn scan_object<EV: EdgeVisitor<VM::VMEdge>>( tls: VMWorkerThread, object: ObjectReference, edge_visitor: &mut EV )
Delegated scanning of a object, visiting each reference field encountered.
The VM shall call edge_visitor.visit_edge
on each reference field.
The VM may skip a reference field if it holds a null reference. If the VM supports tagged references, it must skip tagged reference fields which are not holding references.
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.edge_visitor
: Called back for each edge.
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::VMEdge>
)
fn scan_roots_in_mutator_thread( tls: VMWorkerThread, mutator: &'static mut Mutator<VM>, factory: impl RootsWorkFactory<VM::VMEdge> )
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::VMEdge>
)
fn scan_vm_specific_roots( tls: VMWorkerThread, factory: impl RootsWorkFactory<VM::VMEdge> )
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_edge_enqueuing(
_tls: VMWorkerThread,
_object: ObjectReference
) -> bool
fn support_edge_enqueuing( _tls: VMWorkerThread, _object: ObjectReference ) -> bool
Return true if the given object supports edge 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 edge-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 trace the objects pointed by each field.
The VM shall call object_tracer.trace_object
on the value held in each reference field,
and assign the returned value back to the field. If the VM uses tagged references, the
value passed to object_tracer.trace_object
shall be the ObjectReference
to the object
without any tag bits.
The VM may skip a reference field if it holds a null reference. If the VM supports tagged references, it must skip tagged reference fields which are not holding references.
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 content of each edge.
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.