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}