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