mmtk/plan/
global.rs

1//! The global part of a plan implementation.
2
3use super::PlanConstraints;
4use crate::global_state::GlobalState;
5use crate::mmtk::MMTK;
6use crate::plan::gc_work::{ClearCommonPlanUnlogBits, SetCommonPlanUnlogBits};
7use crate::plan::tracing::ObjectQueue;
8use crate::plan::Mutator;
9use crate::policy::immortalspace::ImmortalSpace;
10use crate::policy::largeobjectspace::LargeObjectSpace;
11use crate::policy::space::{PlanCreateSpaceArgs, Space};
12#[cfg(feature = "vm_space")]
13use crate::policy::vmspace::VMSpace;
14use crate::scheduler::*;
15use crate::util::alloc::allocators::AllocatorSelector;
16use crate::util::copy::{CopyConfig, GCWorkerCopyContext};
17use crate::util::heap::gc_trigger::GCTrigger;
18use crate::util::heap::gc_trigger::SpaceStats;
19use crate::util::heap::layout::Mmapper;
20use crate::util::heap::layout::VMMap;
21use crate::util::heap::HeapMeta;
22use crate::util::heap::VMRequest;
23use crate::util::metadata::log_bit::UnlogBitsOperation;
24use crate::util::metadata::side_metadata::SideMetadataSanity;
25use crate::util::metadata::side_metadata::SideMetadataSpec;
26use crate::util::options::Options;
27use crate::util::options::PlanSelector;
28use crate::util::statistics::stats::Stats;
29use crate::util::{conversions, ObjectReference};
30use crate::util::{VMMutatorThread, VMWorkerThread};
31use crate::vm::*;
32use downcast_rs::Downcast;
33use enum_map::EnumMap;
34use std::sync::atomic::Ordering;
35use std::sync::Arc;
36
37use mmtk_macros::{HasSpaces, PlanTraceObject};
38
39pub fn create_mutator<VM: VMBinding>(
40    tls: VMMutatorThread,
41    mmtk: &'static MMTK<VM>,
42) -> Box<Mutator<VM>> {
43    Box::new(match *mmtk.options.plan {
44        PlanSelector::NoGC => crate::plan::nogc::mutator::create_nogc_mutator(tls, mmtk),
45        PlanSelector::SemiSpace => crate::plan::semispace::mutator::create_ss_mutator(tls, mmtk),
46        PlanSelector::GenCopy => {
47            crate::plan::generational::copying::mutator::create_gencopy_mutator(tls, mmtk)
48        }
49        PlanSelector::GenImmix => {
50            crate::plan::generational::immix::mutator::create_genimmix_mutator(tls, mmtk)
51        }
52        PlanSelector::MarkSweep => crate::plan::marksweep::mutator::create_ms_mutator(tls, mmtk),
53        PlanSelector::Immix => crate::plan::immix::mutator::create_immix_mutator(tls, mmtk),
54        PlanSelector::PageProtect => {
55            crate::plan::pageprotect::mutator::create_pp_mutator(tls, mmtk)
56        }
57        PlanSelector::Lisp2 => {
58            crate::plan::markcompact::lisp2::mutator::create_lisp2_mutator(tls, mmtk)
59        }
60        PlanSelector::StickyImmix => {
61            crate::plan::sticky::immix::mutator::create_stickyimmix_mutator(tls, mmtk)
62        }
63        PlanSelector::LXR => crate::plan::lxr::mutator::create_lxr_mutator(tls, mmtk),
64        PlanSelector::ConcurrentImmix => {
65            crate::plan::concurrent::immix::mutator::create_concurrent_immix_mutator(tls, mmtk)
66        }
67        PlanSelector::OVC => crate::plan::markcompact::ovc::mutator::create_ovc_mutator(tls, mmtk),
68    })
69}
70
71/// Create a plan and the spaces for the plan.
72///
73/// It is very important that in the constructor of each plan (including the constructor of each space),
74/// sft and side metadata is not available for access. If a plan or a space needs to initialize sft or side metadata
75/// in its constructor, it needs to postpone the initialization to [`Plan::initialize_sft`], [`Plan::initialize_side_metadata`],
76/// [`Space::initialize_sft`] or [`Space::initialize_side_metadata`].
77/// If a plan or a space tries to access sft or side metadata in its constructor, it may cause undefined behavior.
78pub fn create_plan<VM: VMBinding>(
79    plan: PlanSelector,
80    args: CreateGeneralPlanArgs<VM>,
81) -> Box<dyn Plan<VM = VM>> {
82    match plan {
83        PlanSelector::NoGC => {
84            Box::new(crate::plan::nogc::NoGC::new(args)) as Box<dyn Plan<VM = VM>>
85        }
86        PlanSelector::SemiSpace => {
87            Box::new(crate::plan::semispace::SemiSpace::new(args)) as Box<dyn Plan<VM = VM>>
88        }
89        PlanSelector::GenCopy => Box::new(crate::plan::generational::copying::GenCopy::new(args))
90            as Box<dyn Plan<VM = VM>>,
91        PlanSelector::GenImmix => Box::new(crate::plan::generational::immix::GenImmix::new(args))
92            as Box<dyn Plan<VM = VM>>,
93        PlanSelector::MarkSweep => {
94            Box::new(crate::plan::marksweep::MarkSweep::new(args)) as Box<dyn Plan<VM = VM>>
95        }
96        PlanSelector::Immix => {
97            Box::new(crate::plan::immix::Immix::new(args)) as Box<dyn Plan<VM = VM>>
98        }
99        PlanSelector::PageProtect => {
100            Box::new(crate::plan::pageprotect::PageProtect::new(args)) as Box<dyn Plan<VM = VM>>
101        }
102        PlanSelector::Lisp2 => {
103            Box::new(crate::plan::markcompact::lisp2::Lisp2::new(args)) as Box<dyn Plan<VM = VM>>
104        }
105        PlanSelector::StickyImmix => {
106            Box::new(crate::plan::sticky::immix::StickyImmix::new(args)) as Box<dyn Plan<VM = VM>>
107        }
108        PlanSelector::LXR => crate::plan::lxr::LXR::new(args) as Box<dyn Plan<VM = VM>>,
109        PlanSelector::ConcurrentImmix => {
110            Box::new(crate::plan::concurrent::immix::ConcurrentImmix::new(args))
111                as Box<dyn Plan<VM = VM>>
112        }
113        PlanSelector::OVC => {
114            Box::new(crate::plan::markcompact::ovc::OVC::new(args)) as Box<dyn Plan<VM = VM>>
115        }
116    }
117}
118
119/// Create thread local GC worker.
120pub fn create_gc_worker_context<VM: VMBinding>(
121    tls: VMWorkerThread,
122    mmtk: &'static MMTK<VM>,
123) -> GCWorkerCopyContext<VM> {
124    GCWorkerCopyContext::<VM>::new(tls, mmtk, mmtk.get_plan().create_copy_config())
125}
126
127/// A plan describes the global core functionality for all memory management schemes.
128/// All global MMTk plans should implement this trait.
129///
130/// The global instance defines and manages static resources
131/// (such as memory and virtual memory resources).
132///
133/// Constructor:
134///
135/// For the constructor of a new plan, there are a few things the constructor _must_ do
136/// (please check existing plans and see what they do in the constructor):
137/// 1. Create a HeapMeta, and use this HeapMeta to initialize all the spaces.
138/// 2. Create a vector of all the side metadata specs with `SideMetadataContext::new_global_specs()`,
139///    the parameter is a vector of global side metadata specs that are specific to the plan.
140/// 3. Initialize all the spaces the plan uses with the heap meta, and the global metadata specs vector.
141/// 4. Invoke the `verify_side_metadata_sanity()` method of the plan.
142///    It will create a `SideMetadataSanity` object, and invoke verify_side_metadata_sanity() for each space (or
143///    invoke verify_side_metadata_sanity() in `CommonPlan`/`BasePlan` for the spaces in the common/base plan).
144///
145/// Methods in this trait:
146///
147/// Only methods that will be overridden by each specific plan should be included in this trait. The trait may
148/// provide a default implementation, and each plan can override the implementation. For methods that won't be
149/// overridden, we should implement those methods in BasePlan (or CommonPlan) and call them from there instead.
150/// We should avoid having methods with the same name in both Plan and BasePlan, as this may confuse people, and
151/// they may call a wrong method by mistake.
152// TODO: Some methods that are not overriden can be moved from the trait to BasePlan.
153pub trait Plan: 'static + HasSpaces + Sync + Downcast {
154    /// Get the plan constraints for the plan.
155    /// This returns a non-constant value. A constant value can be found in each plan's module if needed.
156    fn constraints(&self) -> &'static PlanConstraints;
157
158    /// Create a copy config for this plan. A copying GC plan MUST override this method,
159    /// and provide a valid config.
160    fn create_copy_config(&'static self) -> CopyConfig<Self::VM> {
161        // Use the empty default copy config for non copying GC.
162        CopyConfig::default()
163    }
164
165    /// Get a immutable reference to the base plan. `BasePlan` is included by all the MMTk GC plans.
166    fn base(&self) -> &BasePlan<Self::VM>;
167
168    /// Get a mutable reference to the base plan. `BasePlan` is included by all the MMTk GC plans.
169    fn base_mut(&mut self) -> &mut BasePlan<Self::VM>;
170
171    /// Schedule work for the upcoming GC.
172    fn schedule_collection(&'static self, _scheduler: &GCWorkScheduler<Self::VM>);
173
174    /// Get the common plan. CommonPlan is included by most of MMTk GC plans.
175    fn common(&self) -> &CommonPlan<Self::VM> {
176        panic!("Common Plan not handled!")
177    }
178
179    /// Get a mutable reference to the common plan. See [`Self::common`].
180    fn common_mut(&mut self) -> &mut CommonPlan<Self::VM> {
181        panic!("Common Plan not handled!")
182    }
183
184    /// Return a reference to `GenerationalPlan` to allow
185    /// access methods specific to generational plans if the plan is a generational plan.
186    fn generational(
187        &self,
188    ) -> Option<&dyn crate::plan::generational::global::GenerationalPlan<VM = Self::VM>> {
189        None
190    }
191
192    /// Return a reference to `ConcurrentPlan` to allow
193    /// access methods specific to concurrent plans if the plan is a concurrent plan.
194    fn concurrent(
195        &self,
196    ) -> Option<&dyn crate::plan::concurrent::global::ConcurrentPlan<VM = Self::VM>> {
197        None
198    }
199
200    /// Get the current run time options.
201    fn options(&self) -> &Options {
202        &self.base().options
203    }
204
205    /// Get the allocator mapping between [`crate::AllocationSemantics`] and [`crate::util::alloc::AllocatorSelector`].
206    /// This defines what space this plan will allocate objects into for different semantics.
207    fn get_allocator_mapping(&self) -> &'static EnumMap<AllocationSemantics, AllocatorSelector>;
208
209    /// Called once all mutators have been stopped.  This is called before `Prepare`, which is right
210    /// before root scanning starts, at the beginning of a GC pause.
211    ///
212    /// Plans that need to do per-pause setup (e.g. resetting mark tables, flushing mutator state)
213    /// can override this.
214    ///
215    /// A plan that overrides this function need to manage the invocation of
216    /// `GCTriggerPolicy::on_gc_start` at the proper timing for the plan.
217    fn on_pause_start(&self, mmtk: &'static MMTK<Self::VM>) {
218        assert!(
219            self.concurrent().is_none(),
220            "ConcurrentPlan must override on_pause_start"
221        );
222        mmtk.gc_trigger.policy.on_gc_start(mmtk);
223    }
224
225    /// Prepare the plan before a GC. This is invoked in an initial step in the GC.
226    /// This is invoked once per GC by one worker thread. `tls` is the worker thread that executes this method.
227    fn prepare(&mut self, tls: VMWorkerThread);
228
229    /// Prepare a worker for a GC. Each worker has its own prepare method. This hook is for plan-specific
230    /// per-worker preparation. This method is invoked once per worker by the worker thread passed as the argument.
231    fn prepare_worker(&self, _worker: &mut GCWorker<Self::VM>) {}
232
233    /// Release the plan after transitive closure. A plan can implement this method to call each policy's release,
234    /// or create any work packet that should be done in release.
235    /// This is invoked once per GC by one worker thread. `tls` is the worker thread that executes this method.
236    fn release(&mut self, tls: VMWorkerThread);
237
238    /// Called at the end of a GC pause.  It is guaranteed that there is no further work for this
239    /// pause.  This is invoked once per pause by one worker thread.  `tls` is the worker thread
240    /// that executes this method.
241    ///
242    /// Plans that need to do per-pause teardown (e.g. recording pause-end statistics) can override
243    /// this.
244    ///
245    /// A plan that overrides this function need to do whatever the default implementation does at
246    /// the proper timing for the plan, such as calling `CommonPlan::on_pause_end`, and selectively
247    /// call `GCTriggerPolicy::on_gc_end` if the pause is the end of a GC.
248    fn on_pause_end(&mut self, mmtk: &'static MMTK<Self::VM>, tls: VMWorkerThread) {
249        self.common_mut().on_pause_end(tls);
250        assert!(
251            self.concurrent().is_none(),
252            "ConcurrentPlan must override on_pause_end"
253        );
254        mmtk.gc_trigger.policy.on_gc_end(mmtk);
255    }
256
257    /// Notify the plan that an emergency collection will happen. The plan should try to free as much memory as possible.
258    /// The default implementation will force a full heap collection for generational plans.
259    fn notify_emergency_collection(&self) {
260        if let Some(gen) = self.generational() {
261            gen.force_full_heap_collection();
262        }
263    }
264
265    /// Ask the plan if they would trigger a GC. If MMTk is in charge of triggering GCs, this method is called
266    /// periodically during allocation. However, MMTk may delegate the GC triggering decision to the runtime,
267    /// in which case, this method may not be called. This method returns true to trigger a collection.
268    ///
269    /// # Arguments
270    /// * `space_full`: the allocation to a specific space failed, must recover pages within 'space'.
271    /// * `space`: an option to indicate if there is a space that has failed in an allocation.
272    fn collection_required(&self, space_full: bool, space: Option<SpaceStats<Self::VM>>) -> bool;
273
274    // Note: The following methods are about page accounting. The default implementation should
275    // work fine for non-copying plans. For copying plans, the plan should override any of these methods
276    // if necessary.
277
278    /// Get the number of pages that are reserved, including pages used by MMTk spaces, pages that
279    /// will be used (e.g. for copying), and live pages allocated outside MMTk spaces as reported
280    /// by the VM binding.
281    fn get_reserved_pages(&self) -> usize {
282        let used_pages = self.get_used_pages();
283        let collection_reserve = self.get_collection_reserved_pages();
284        let vm_live_bytes = <Self::VM as VMBinding>::VMCollection::vm_live_bytes();
285        // Note that `vm_live_bytes` may not be the exact number of bytes in whole pages.  The VM
286        // binding is allowed to return an approximate value if it is expensive or impossible to
287        // compute the exact number of pages occupied.
288        let vm_live_pages = conversions::bytes_to_pages_up(vm_live_bytes);
289        let total = used_pages + collection_reserve + vm_live_pages;
290
291        trace!(
292            "Reserved pages = {}, used pages: {}, collection reserve: {}, VM live pages: {}",
293            total,
294            used_pages,
295            collection_reserve,
296            vm_live_pages,
297        );
298
299        total
300    }
301
302    /// Get the total number of pages for the heap.
303    fn get_total_pages(&self) -> usize {
304        self.base()
305            .gc_trigger
306            .policy
307            .get_current_heap_size_in_pages()
308    }
309
310    /// Get the number of pages that are still available for use. The available pages
311    /// should always be positive or 0.
312    fn get_available_pages(&self) -> usize {
313        let reserved_pages = self.get_reserved_pages();
314        let total_pages = self.get_total_pages();
315
316        // It is possible that the reserved pages is larger than the total pages so we are doing
317        // a saturating subtraction to make sure we return a non-negative number.
318        // For example,
319        // 1. our GC trigger checks if reserved pages is more than total pages.
320        // 2. when the heap is almost full of live objects (such as in the case of an OOM) and we are doing a copying GC, it is possible
321        //    the reserved pages is larger than total pages after the copying GC (the reserved pages after a GC
322        //    may be larger than the reserved pages before a GC, as we may end up using more memory for thread local
323        //    buffers for copy allocators).
324        // 3. the binding disabled GC, and we end up over-allocating beyond the total pages determined by the GC trigger.
325        let available_pages = total_pages.saturating_sub(reserved_pages);
326        trace!(
327            "Total pages = {}, reserved pages = {}, available pages = {}",
328            total_pages,
329            reserved_pages,
330            available_pages,
331        );
332        available_pages
333    }
334
335    /// Get the number of pages that are reserved for collection. By default, we return 0.
336    /// For copying plans, they need to override this and calculate required pages to complete
337    /// a copying GC.
338    fn get_collection_reserved_pages(&self) -> usize {
339        0
340    }
341
342    /// Get the number of pages that are used.
343    fn get_used_pages(&self) -> usize;
344
345    /// Get the number of pages that are NOT used. This is clearly different from available pages.
346    /// Free pages are unused, but some of them may have been reserved for some reason.
347    fn get_free_pages(&self) -> usize {
348        let total_pages = self.get_total_pages();
349        let used_pages = self.get_used_pages();
350
351        // It is possible that the used pages is larger than the total pages, so we use saturating
352        // subtraction.  See the comments in `get_available_pages`.
353        total_pages.saturating_sub(used_pages)
354    }
355
356    /// Return whether last GC was an exhaustive attempt to collect the heap.
357    /// For example, for generational GCs, minor collection is not an exhaustive collection.
358    /// For example, for Immix, fast collection (no defragmentation) is not an exhaustive collection.
359    fn last_collection_was_exhaustive(&self) -> bool {
360        true
361    }
362
363    /// Return the work bucket stage in which mutator (and VM) roots should be scanned for this
364    /// plan. By default, roots are scanned in the `Prepare` stage, but concurrent/incremental
365    /// plans may schedule root scanning into a different stage (e.g. alongside reference
366    /// counting increments).
367    fn root_scanning_stage(&self) -> WorkBucketStage {
368        WorkBucketStage::Prepare
369    }
370
371    /// Return whether the current GC may move any object.  The VM binding can make use of this
372    /// information and choose to or not to update some data structures that record the addresses
373    /// of objects.
374    ///
375    /// This function is callable during a GC.  From the VM binding's point of view, the information
376    /// of whether the current GC moves object or not is available since `Collection::stop_mutators`
377    /// is called, and remains available until (but not including) `resume_mutators` at which time
378    /// the current GC has just finished.
379    fn current_gc_may_move_object(&self) -> bool;
380
381    /// An object is firstly reached by a sanity GC. So the object is reachable
382    /// in the current GC, and all the GC work has been done for the object (such as
383    /// tracing and releasing). A plan can implement this to
384    /// use plan specific semantics to check if the object is sane.
385    /// Return true if the object is considered valid by the plan.
386    fn sanity_check_object(&self, _object: ObjectReference) -> bool {
387        true
388    }
389
390    /// Call `space.verify_side_metadata_sanity` for all spaces in this plan.
391    fn verify_side_metadata_sanity(&self) {
392        let mut side_metadata_sanity_checker = SideMetadataSanity::new();
393        self.for_each_space(&mut |space| {
394            space.verify_side_metadata_sanity(&mut side_metadata_sanity_checker);
395        })
396    }
397
398    /// Call `space.initialize_sft` for all spaces in this plan, and notify the SFT map about the creation of each space.
399    /// This method should only be called after 1. side metadata is initialized (as some SFT maps may use side metadata), 2. the plan is created in the heap and won't be moved,
400    /// and 3. the side metadata sanity is initialized (otherwise we may try access side metadata and trigger sanity check before side metadata sanity is initialized)
401    fn initialize_sft(&self) {
402        let sft_map: &mut dyn crate::policy::sft_map::SFTMap =
403            unsafe { crate::mmtk::SFT_MAP.get_mut() }.as_mut();
404        self.for_each_space(&mut |s| {
405            sft_map.notify_space_creation(s.as_sft());
406            s.initialize_sft(sft_map);
407        });
408    }
409
410    /// Call `space.initialize_side_metadata` for all spaces in this plan.
411    /// This is called after the plan is created in the heap and won't be moved, and after side metadata is initialized.
412    /// If a plan needs to access side metadata during space construction, it can override this method for its own initialization.
413    fn initialize_side_metadata(&self) {
414        self.for_each_space(&mut |s| s.initialize_side_metadata());
415    }
416}
417
418impl_downcast!(Plan assoc VM);
419
420/**
421BasePlan should contain all plan-related state and functions that are _fundamental_ to _all_ plans.  These include VM-specific (but not plan-specific) features such as a code space or vm space, which are fundamental to all plans for a given VM.  Features that are common to _many_ (but not intrinsically _all_) plans should instead be included in CommonPlan.
422*/
423#[derive(HasSpaces, PlanTraceObject)]
424pub struct BasePlan<VM: VMBinding> {
425    pub(crate) global_state: Arc<GlobalState>,
426    pub options: Arc<Options>,
427    pub gc_trigger: Arc<GCTrigger<VM>>,
428    pub scheduler: Arc<GCWorkScheduler<VM>>,
429
430    // Spaces in base plan
431    #[cfg(feature = "code_space")]
432    #[space]
433    pub code_space: ImmortalSpace<VM>,
434    #[cfg(feature = "code_space")]
435    #[space]
436    pub code_lo_space: ImmortalSpace<VM>,
437    #[cfg(feature = "ro_space")]
438    #[space]
439    pub ro_space: ImmortalSpace<VM>,
440
441    /// A VM space is a space allocated and populated by the VM.  Currently it is used by JikesRVM
442    /// for boot image.
443    ///
444    /// If VM space is present, it has some special interaction with the
445    /// `memory_manager::is_mmtk_object` and the `memory_manager::is_in_mmtk_spaces` functions.
446    ///
447    /// -   The functions `is_mmtk_object` and `find_object_from_internal_pointer` require
448    ///     the valid object (VO) bit side metadata to identify objects.
449    ///     If the binding maintains the VO bit for objects in VM spaces, those functions will work accordingly.
450    ///     Otherwise, calling them is undefined behavior.
451    ///
452    /// -   The `is_in_mmtk_spaces` currently returns `true` if the given object reference is in
453    ///     the VM space.
454    #[cfg(feature = "vm_space")]
455    #[space]
456    pub vm_space: VMSpace<VM>,
457}
458
459/// Args needed for creating any plan. This includes a set of contexts from MMTK or global. This
460/// is passed to each plan's constructor.
461pub struct CreateGeneralPlanArgs<'a, VM: VMBinding> {
462    pub vm_map: &'static dyn VMMap,
463    pub mmapper: &'static dyn Mmapper,
464    pub options: Arc<Options>,
465    pub state: Arc<GlobalState>,
466    pub gc_trigger: Arc<crate::util::heap::gc_trigger::GCTrigger<VM>>,
467    pub scheduler: Arc<GCWorkScheduler<VM>>,
468    pub stats: &'a Stats,
469    pub heap: &'a mut HeapMeta,
470}
471
472/// Args needed for creating a specific plan. This includes plan-specific args, such as plan constrainst
473/// and their global side metadata specs. This is created in each plan's constructor, and will be passed
474/// to `CommonPlan` or `BasePlan`. Also you can create `PlanCreateSpaceArg` from this type, and use that
475/// to create spaces.
476pub struct CreateSpecificPlanArgs<'a, VM: VMBinding> {
477    pub global_args: CreateGeneralPlanArgs<'a, VM>,
478    pub constraints: &'static PlanConstraints,
479    pub global_side_metadata_specs: Vec<SideMetadataSpec>,
480}
481
482impl<VM: VMBinding> CreateSpecificPlanArgs<'_, VM> {
483    /// Get a PlanCreateSpaceArgs that can be used to create a space
484    pub fn _get_space_args(
485        &mut self,
486        name: &'static str,
487        zeroed: bool,
488        permission_exec: bool,
489        unlog_allocated_object: bool,
490        unlog_traced_object: bool,
491        vmrequest: VMRequest,
492    ) -> PlanCreateSpaceArgs<'_, VM> {
493        PlanCreateSpaceArgs {
494            name,
495            zeroed,
496            permission_exec,
497            vmrequest,
498            unlog_allocated_object,
499            unlog_traced_object,
500            global_side_metadata_specs: self.global_side_metadata_specs.clone(),
501            vm_map: self.global_args.vm_map,
502            mmapper: self.global_args.mmapper,
503            heap: self.global_args.heap,
504            constraints: self.constraints,
505            gc_trigger: self.global_args.gc_trigger.clone(),
506            scheduler: self.global_args.scheduler.clone(),
507            options: self.global_args.options.clone(),
508            global_state: self.global_args.state.clone(),
509        }
510    }
511
512    // The following are some convenience methods for common presets.
513    // These are not an exhaustive list -- it is just common presets that are used by most plans.
514
515    /// Get a preset for a nursery space (where young objects are located).
516    pub fn get_nursery_space_args(
517        &mut self,
518        name: &'static str,
519        zeroed: bool,
520        permission_exec: bool,
521        vmrequest: VMRequest,
522    ) -> PlanCreateSpaceArgs<'_, VM> {
523        // Objects are allocatd as young, and when traced, they stay young. If they are copied out of the nursery space, they will be moved to a mature space,
524        // and log bits will be set in that case by the mature space.
525        self._get_space_args(name, zeroed, permission_exec, false, false, vmrequest)
526    }
527
528    /// Get a preset for a mature space (where mature objects are located).
529    pub fn get_mature_space_args(
530        &mut self,
531        name: &'static str,
532        zeroed: bool,
533        permission_exec: bool,
534        vmrequest: VMRequest,
535    ) -> PlanCreateSpaceArgs<'_, VM> {
536        // Objects are allocated as mature (pre-tenured), and when traced, they stay mature.
537        // If an object gets copied into a mature space, the object is also mature,
538        self._get_space_args(name, zeroed, permission_exec, true, true, vmrequest)
539    }
540
541    // Get a preset for a mixed age space (where both young and mature objects are located).
542    pub fn get_mixed_age_space_args(
543        &mut self,
544        name: &'static str,
545        zeroed: bool,
546        permission_exec: bool,
547        vmrequest: VMRequest,
548    ) -> PlanCreateSpaceArgs<'_, VM> {
549        // Objects are allocated as young, and when traced, they become mature objects.
550        self._get_space_args(name, zeroed, permission_exec, false, true, vmrequest)
551    }
552
553    /// Get a preset for spaces in a non-generational plan.
554    pub fn get_normal_space_args(
555        &mut self,
556        name: &'static str,
557        zeroed: bool,
558        permission_exec: bool,
559        vmrequest: VMRequest,
560    ) -> PlanCreateSpaceArgs<'_, VM> {
561        // Non generational plan: we do not use any of the flags about log bits.
562        self._get_space_args(name, zeroed, permission_exec, false, false, vmrequest)
563    }
564
565    /// Get a preset for spaces in [`crate::plan::global::CommonPlan`].
566    /// Spaces like LOS which may include both young and mature objects should not use this method.
567    pub fn get_common_space_args(
568        &mut self,
569        generational: bool,
570        name: &'static str,
571    ) -> PlanCreateSpaceArgs<'_, VM> {
572        self.get_base_space_args(
573            generational,
574            name,
575            false, // Common spaces are not executable.
576        )
577    }
578
579    /// Get a preset for spaces in [`crate::plan::global::BasePlan`].
580    pub fn get_base_space_args(
581        &mut self,
582        generational: bool,
583        name: &'static str,
584        permission_exec: bool,
585    ) -> PlanCreateSpaceArgs<'_, VM> {
586        if generational {
587            // In generational plans, common/base spaces behave like a mature space:
588            // * the objects in these spaces are not traced in a nursery GC
589            // * the log bits for the objects are maintained exactly the same as a mature space.
590            // Thus we consider them as mature spaces.
591            self.get_mature_space_args(name, true, permission_exec, VMRequest::discontiguous())
592        } else {
593            self.get_normal_space_args(name, true, permission_exec, VMRequest::discontiguous())
594        }
595    }
596}
597
598impl<VM: VMBinding> BasePlan<VM> {
599    #[allow(unused_mut)] // 'args' only needs to be mutable for certain features
600    pub fn new(mut args: CreateSpecificPlanArgs<VM>) -> BasePlan<VM> {
601        let _generational = args.constraints.generational;
602        BasePlan {
603            #[cfg(feature = "code_space")]
604            code_space: ImmortalSpace::new(args.get_base_space_args(
605                _generational,
606                "code_space",
607                true,
608            )),
609            #[cfg(feature = "code_space")]
610            code_lo_space: ImmortalSpace::new(args.get_base_space_args(
611                _generational,
612                "code_lo_space",
613                true,
614            )),
615            #[cfg(feature = "ro_space")]
616            ro_space: ImmortalSpace::new(args.get_base_space_args(
617                _generational,
618                "ro_space",
619                false,
620            )),
621            #[cfg(feature = "vm_space")]
622            vm_space: VMSpace::new(args.get_base_space_args(
623                _generational,
624                "vm_space",
625                false, // it doesn't matter -- we are not mmapping for VM space.
626            )),
627
628            global_state: args.global_args.state.clone(),
629            gc_trigger: args.global_args.gc_trigger,
630            options: args.global_args.options,
631            scheduler: args.global_args.scheduler,
632        }
633    }
634
635    // Depends on what base spaces we use, unsync may be unused.
636    pub fn get_used_pages(&self) -> usize {
637        // Depends on what base spaces we use, pages may be unchanged.
638        #[allow(unused_mut)]
639        let mut pages = 0;
640
641        #[cfg(feature = "code_space")]
642        {
643            pages += self.code_space.reserved_pages();
644            pages += self.code_lo_space.reserved_pages();
645        }
646        #[cfg(feature = "ro_space")]
647        {
648            pages += self.ro_space.reserved_pages();
649        }
650
651        // If we need to count malloc'd size as part of our heap, we add it here.
652        #[cfg(feature = "malloc_counted_size")]
653        {
654            pages += self.global_state.get_malloc_bytes_in_pages();
655        }
656
657        // The VM space may be used as an immutable boot image, in which case, we should not count
658        // it as part of the heap size.
659        pages
660    }
661
662    pub fn prepare(&mut self, _tls: VMWorkerThread, _full_heap: bool) {
663        #[cfg(feature = "code_space")]
664        self.code_space.prepare();
665        #[cfg(feature = "code_space")]
666        self.code_lo_space.prepare();
667        #[cfg(feature = "ro_space")]
668        self.ro_space.prepare();
669        #[cfg(feature = "vm_space")]
670        self.vm_space.prepare();
671    }
672
673    pub fn release(&mut self, _tls: VMWorkerThread, _full_heap: bool) {
674        #[cfg(feature = "code_space")]
675        self.code_space.release();
676        #[cfg(feature = "code_space")]
677        self.code_lo_space.release();
678        #[cfg(feature = "ro_space")]
679        self.ro_space.release();
680        #[cfg(feature = "vm_space")]
681        self.vm_space.release();
682    }
683
684    pub fn clear_side_log_bits(&self) {
685        #[cfg(feature = "code_space")]
686        self.code_space.clear_side_log_bits();
687        #[cfg(feature = "code_space")]
688        self.code_lo_space.clear_side_log_bits();
689        #[cfg(feature = "ro_space")]
690        self.ro_space.clear_side_log_bits();
691        #[cfg(feature = "vm_space")]
692        self.vm_space.clear_side_log_bits();
693    }
694
695    pub fn set_side_log_bits(&self) {
696        #[cfg(feature = "code_space")]
697        self.code_space.set_side_log_bits();
698        #[cfg(feature = "code_space")]
699        self.code_lo_space.set_side_log_bits();
700        #[cfg(feature = "ro_space")]
701        self.ro_space.set_side_log_bits();
702        #[cfg(feature = "vm_space")]
703        self.vm_space.set_side_log_bits();
704    }
705
706    pub fn on_pause_end(&mut self, _tls: VMWorkerThread) {
707        // Do nothing here. None of the spaces needs on_pause_end.
708    }
709
710    pub(crate) fn collection_required<P: Plan>(&self, plan: &P, space_full: bool) -> bool {
711        let stress_force_gc =
712            crate::util::heap::gc_trigger::GCTrigger::<VM>::should_do_stress_gc_inner(
713                &self.global_state,
714                &self.options,
715            );
716        if stress_force_gc {
717            debug!(
718                "Stress GC: allocation_bytes = {}, stress_factor = {}",
719                self.global_state.allocation_bytes.load(Ordering::Relaxed),
720                *self.options.stress_factor
721            );
722            debug!("Doing stress GC");
723            self.global_state
724                .allocation_bytes
725                .store(0, Ordering::SeqCst);
726        }
727
728        debug!(
729            "self.get_reserved_pages()={}, self.get_total_pages()={}",
730            plan.get_reserved_pages(),
731            plan.get_total_pages()
732        );
733        // Check if we reserved more pages (including the collection copy reserve)
734        // than the heap's total pages. In that case, we will have to do a GC.
735        let heap_full = plan.base().gc_trigger.is_heap_full();
736
737        space_full || stress_force_gc || heap_full
738    }
739}
740
741cfg_if::cfg_if! {
742    // Use immortal or mark sweep as the non moving space if the features are enabled. Otherwise use Immix.
743    if #[cfg(feature = "immortal_as_nonmoving")] {
744        pub type NonMovingSpace<VM> = crate::policy::immortalspace::ImmortalSpace<VM>;
745    } else if #[cfg(feature = "marksweep_as_nonmoving")] {
746        pub type NonMovingSpace<VM> = crate::policy::marksweepspace::native_ms::MarkSweepSpace<VM>;
747    } else {
748        pub type NonMovingSpace<VM> = crate::policy::immix::ImmixSpace<VM>;
749    }
750}
751
752/**
753CommonPlan is for representing state and features used by _many_ plans, but that are not fundamental to _all_ plans.  Examples include the Large Object Space and an Immortal space.  Features that are fundamental to _all_ plans must be included in BasePlan.
754*/
755#[derive(HasSpaces, PlanTraceObject)]
756pub struct CommonPlan<VM: VMBinding> {
757    #[space]
758    pub immortal: ImmortalSpace<VM>,
759    #[space]
760    pub los: LargeObjectSpace<VM>,
761    #[space]
762    #[cfg_attr(
763        not(any(feature = "immortal_as_nonmoving", feature = "marksweep_as_nonmoving")),
764        post_scan
765    )] // Immix space needs post_scan
766    pub nonmoving: NonMovingSpace<VM>,
767    #[parent]
768    pub base: BasePlan<VM>,
769}
770
771impl<VM: VMBinding> CommonPlan<VM> {
772    pub fn new(mut args: CreateSpecificPlanArgs<VM>) -> CommonPlan<VM> {
773        let needs_log_bit = args.constraints.needs_log_bit;
774        let generational = args.constraints.generational;
775        CommonPlan {
776            immortal: ImmortalSpace::new(args.get_common_space_args(generational, "immortal")),
777            los: LargeObjectSpace::new(
778                // LOS is a bit special, as it is a mixed age space. It has a logical nursery.
779                if generational {
780                    args.get_mixed_age_space_args("los", true, false, VMRequest::discontiguous())
781                } else {
782                    args.get_normal_space_args("los", true, false, VMRequest::discontiguous())
783                },
784                false,
785                needs_log_bit,
786            ),
787            nonmoving: Self::new_nonmoving_space(&mut args),
788            base: BasePlan::new(args),
789        }
790    }
791
792    pub fn get_used_pages(&self) -> usize {
793        self.immortal.reserved_pages()
794            + self.los.reserved_pages()
795            + self.nonmoving.reserved_pages()
796            + self.base.get_used_pages()
797    }
798
799    pub fn prepare(&mut self, tls: VMWorkerThread, full_heap: bool) {
800        self.immortal.prepare();
801        self.los.prepare(full_heap);
802        self.prepare_nonmoving_space(full_heap);
803        self.base.prepare(tls, full_heap)
804    }
805
806    pub fn release(&mut self, tls: VMWorkerThread, full_heap: bool) {
807        self.immortal.release();
808        self.los.release(full_heap);
809        self.release_nonmoving_space(full_heap);
810        self.base.release(tls, full_heap)
811    }
812
813    pub(crate) fn schedule_unlog_bits_op(&mut self, unlog_bits_op: UnlogBitsOperation) {
814        if VM::VMObjectModel::GLOBAL_LOG_BIT_SPEC.is_on_side() {
815            // # Safety: CommonPlan reference is always valid within this collection cycle.
816            let common_plan = unsafe { &*(self as *const CommonPlan<VM>) };
817
818            match unlog_bits_op {
819                UnlogBitsOperation::NoOp => {}
820                UnlogBitsOperation::BulkSet => {
821                    self.base.scheduler.work_buckets[WorkBucketStage::Prepare]
822                        .add(SetCommonPlanUnlogBits { common_plan });
823                }
824                UnlogBitsOperation::BulkClear => {
825                    self.base.scheduler.work_buckets[WorkBucketStage::Release]
826                        .add(ClearCommonPlanUnlogBits { common_plan });
827                }
828            }
829        }
830    }
831
832    pub fn clear_side_log_bits(&self) {
833        self.immortal.clear_side_log_bits();
834        self.los.clear_side_log_bits();
835        self.base.clear_side_log_bits();
836    }
837
838    pub fn set_side_log_bits(&self) {
839        self.immortal.set_side_log_bits();
840        self.los.set_side_log_bits();
841        self.base.set_side_log_bits();
842    }
843
844    pub fn on_pause_end(&mut self, tls: VMWorkerThread) {
845        self.end_of_gc_nonmoving_space();
846        self.base.on_pause_end(tls);
847    }
848
849    pub fn get_immortal(&self) -> &ImmortalSpace<VM> {
850        &self.immortal
851    }
852
853    pub fn get_los(&self) -> &LargeObjectSpace<VM> {
854        &self.los
855    }
856
857    pub fn get_nonmoving(&self) -> &NonMovingSpace<VM> {
858        &self.nonmoving
859    }
860
861    fn new_nonmoving_space(args: &mut CreateSpecificPlanArgs<VM>) -> NonMovingSpace<VM> {
862        let space_args = args.get_common_space_args(args.constraints.generational, "nonmoving");
863        cfg_if::cfg_if! {
864            if #[cfg(any(feature = "immortal_as_nonmoving", feature = "marksweep_as_nonmoving"))] {
865                NonMovingSpace::new(space_args)
866            } else {
867                // Immix requires extra args.
868                NonMovingSpace::new(
869                    space_args,
870                    crate::policy::immix::ImmixSpaceArgs {
871                        mixed_age: false,
872                        never_move_objects: true,
873                    },
874                )
875            }
876        }
877    }
878
879    #[allow(clippy::needless_return)]
880    pub(crate) fn prepare_nonmoving_space(&mut self, _full_heap: bool) {
881        // FIXME: We need to handle nonmoving space properly.
882        // Nonmoving space is a bit special for LXR, as it could be a second ImmixSpace (as opposed to the default ImmixSpace).
883        // It is arguable whether we should use an LXR ImmixSpace here, or use a normal Immix space.
884        // If we use an LXR ImmixSpace, we don't have a test case right now to know its correctness.
885        // If we use a normal ImmixSpace, our side metadata sanity does not allow this right, as both LXR ImmixSpace and normal
886        // ImmixSpace are ImmixSpace, and our sanity expects them to use a same set of side metadata.
887        // This might be another reason why LXR ImmixSpace should be a separate policy.
888        if *self.base.options.plan == PlanSelector::LXR {
889            return;
890        }
891
892        cfg_if::cfg_if! {
893            if #[cfg(feature = "immortal_as_nonmoving")] {
894                self.nonmoving.prepare();
895            } else if #[cfg(feature = "marksweep_as_nonmoving")] {
896                self.nonmoving.prepare(_full_heap);
897            } else {
898                self.nonmoving.prepare(_full_heap, None, UnlogBitsOperation::NoOp);
899            }
900        }
901    }
902
903    #[allow(clippy::needless_return)]
904    pub(crate) fn release_nonmoving_space(&mut self, _full_heap: bool) {
905        // FIXME: We need to handle nonmoving space properly.
906        // See comments in prepare_non_moving_space
907        if *self.base.options.plan == PlanSelector::LXR {
908            return;
909        }
910
911        cfg_if::cfg_if! {
912            if #[cfg(feature = "immortal_as_nonmoving")] {
913                self.nonmoving.release();
914            } else if #[cfg(feature = "marksweep_as_nonmoving")] {
915                self.nonmoving.prepare(_full_heap);
916            } else {
917                self.nonmoving.release(_full_heap, UnlogBitsOperation::NoOp);
918            }
919        }
920    }
921
922    #[allow(clippy::needless_return)]
923    pub(crate) fn end_of_gc_nonmoving_space(&mut self) {
924        // FIXME: We need to handle nonmoving space properly.
925        // See comments in prepare_non_moving_space
926        if *self.base.options.plan == PlanSelector::LXR {
927            return;
928        }
929        cfg_if::cfg_if! {
930            if #[cfg(feature = "immortal_as_nonmoving")] {
931                // Nothing we need to do for immortal space.
932            } else if #[cfg(feature = "marksweep_as_nonmoving")] {
933                self.nonmoving.end_of_gc();
934            } else {
935                self.nonmoving.end_of_gc();
936            }
937        }
938    }
939}
940
941use crate::policy::gc_work::TraceKind;
942use crate::vm::VMBinding;
943
944/// A trait for anything that contains spaces.
945/// Examples include concrete plans as well as `Gen`, `CommonPlan` and `BasePlan`.
946/// All plans must implement this trait.
947///
948/// This trait provides methods for enumerating spaces in a struct, including spaces in nested
949/// struct.
950///
951/// This trait can be implemented automatically by adding the `#[derive(HasSpaces)]` attribute to a
952/// struct.  It uses the derive macro defined in the `mmtk-macros` crate.
953///
954/// This trait visits spaces as `dyn`, so it should only be used when performance is not critical.
955/// For performance critical methods that visit spaces in a plan, such as `trace_object`, it is
956/// recommended to define a trait (such as `PlanTraceObject`) for concrete plans to implement, and
957/// implement (by hand or automatically) the method without `dyn`.
958pub trait HasSpaces {
959    // The type of the VM.
960    type VM: VMBinding;
961
962    /// Visit each space field immutably.
963    ///
964    /// If `Self` contains nested fields that contain more spaces, this method shall visit spaces
965    /// in the outer struct first.
966    fn for_each_space(&self, func: &mut dyn FnMut(&dyn Space<Self::VM>));
967
968    /// Visit each space field mutably.
969    ///
970    /// If `Self` contains nested fields that contain more spaces, this method shall visit spaces
971    /// in the outer struct first.
972    fn for_each_space_mut(&mut self, func: &mut dyn FnMut(&mut dyn Space<Self::VM>));
973}
974
975/// A plan that uses [`PlanTrace`] needs to provide an implementation for this trait.
976/// Generally a plan does not need to manually implement this trait. Instead, we provide
977/// a procedural macro that helps generate an implementation. Please check `macros/trace_object`.
978///
979/// A plan could also manually implement this trait. For the sake of performance, the implementation
980/// of this trait should mark methods as `[inline(always)]`.
981///
982/// [`PlanTrace`]: crate::plan::tracing::PlanTrace
983pub trait PlanTraceObject<VM: VMBinding> {
984    /// Trace objects in the plan.
985    ///
986    /// See [`crate::plan::tracing::Trace::trace_object`].
987    fn trace_object<Q: ObjectQueue, const KIND: TraceKind>(
988        &self,
989        queue: &mut Q,
990        object: ObjectReference,
991        worker: &mut GCWorker<VM>,
992    ) -> ObjectReference;
993
994    /// Post-scan objects in the plan.
995    ///
996    /// See [`crate::plan::tracing::Trace::post_scan_object`].
997    fn post_scan_object(&self, object: ObjectReference);
998
999    /// Whether objects in this plan may move.
1000    ///
1001    /// See [`crate::plan::tracing::Trace::post_scan_object`].
1002    fn may_move_objects<const KIND: TraceKind>() -> bool;
1003}
1004
1005use enum_map::Enum;
1006/// Allocation semantics that MMTk provides.
1007/// Each allocation request requires a desired semantic for the object to allocate.
1008#[repr(i32)]
1009#[derive(Clone, Copy, Debug, Enum, PartialEq, Eq)]
1010pub enum AllocationSemantics {
1011    /// The default semantic. This means there is no specific requirement for the allocation.
1012    /// The actual semantic of the default will depend on the GC plan in use.
1013    Default = 0,
1014    /// Immortal objects will not be reclaimed. MMTk still traces immortal objects, but will not
1015    /// reclaim the objects even if they are dead.
1016    Immortal = 1,
1017    /// Large objects. It is usually desirable to allocate large objects specially. Large objects
1018    /// are allocated with page granularity and will not be moved.
1019    /// Each plan provides `max_non_los_default_alloc_bytes` (see [`crate::plan::PlanConstraints`]),
1020    /// which defines a threshold for objects that can be allocated with the default semantic. Any object that is larger than the
1021    /// threshold must be allocated with the `Los` semantic.
1022    /// This semantic may get removed and MMTk will transparently allocate into large object space for large objects.
1023    Los = 2,
1024    /// Code objects have execution permission.
1025    /// Note that we do not currently support this semantic.
1026    Code = 3,
1027    /// Read-only objects cannot be mutated once it is initialized.
1028    /// Note that we do not currently support this semantic.
1029    ReadOnly = 4,
1030    /// Los + Code.
1031    /// Note that we do not currently support this semantic.
1032    LargeCode = 5,
1033    /// Non moving objects will not be moved by GC.
1034    NonMoving = 6,
1035}