mmtk/plan/generational/immix/
global.rs

1use super::gc_work::GenImmixMatureGCWorkContext;
2use super::gc_work::GenImmixNurseryGCWorkContext;
3use crate::plan::generational::global::CommonGenPlan;
4use crate::plan::generational::global::GenerationalPlan;
5use crate::plan::global::BasePlan;
6use crate::plan::global::CommonPlan;
7use crate::plan::global::CreateGeneralPlanArgs;
8use crate::plan::global::CreateSpecificPlanArgs;
9use crate::plan::AllocationSemantics;
10use crate::plan::Plan;
11use crate::plan::PlanConstraints;
12use crate::policy::gc_work::TraceKind;
13use crate::policy::immix::defrag::StatsForDefrag;
14use crate::policy::immix::ImmixSpace;
15use crate::policy::immix::ImmixSpaceArgs;
16use crate::policy::immix::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST};
17use crate::policy::space::Space;
18use crate::scheduler::GCWorkScheduler;
19use crate::scheduler::GCWorker;
20use crate::util::alloc::allocators::AllocatorSelector;
21use crate::util::copy::*;
22use crate::util::heap::gc_trigger::SpaceStats;
23use crate::util::heap::VMRequest;
24use crate::util::metadata::log_bit::UnlogBitsOperation;
25use crate::util::Address;
26use crate::util::ObjectReference;
27use crate::util::VMWorkerThread;
28use crate::vm::*;
29use crate::ObjectQueue;
30use crate::MMTK;
31
32use enum_map::EnumMap;
33use std::sync::atomic::AtomicBool;
34use std::sync::atomic::Ordering;
35
36use mmtk_macros::{HasSpaces, PlanTraceObject};
37
38/// Generational immix. This implements the functionality of a two-generation copying
39/// collector where the higher generation is an immix space.
40/// See the PLDI'08 paper by Blackburn and McKinley for a description
41/// of the algorithm: <http://doi.acm.org/10.1145/1375581.1375586>.
42#[derive(HasSpaces, PlanTraceObject)]
43pub struct GenImmix<VM: VMBinding> {
44    /// Generational plan, which includes a nursery space and operations related with nursery.
45    #[parent]
46    pub gen: CommonGenPlan<VM>,
47    /// An immix space as the mature space.
48    #[post_scan]
49    #[space]
50    #[copy_semantics(CopySemantics::Mature)]
51    pub immix_space: ImmixSpace<VM>,
52    /// Whether the last GC was a defrag GC for the immix space.
53    pub last_gc_was_defrag: AtomicBool,
54    /// Whether the last GC was a full heap GC
55    pub last_gc_was_full_heap: AtomicBool,
56}
57
58/// The plan constraints for the generational immix plan.
59pub const GENIMMIX_CONSTRAINTS: PlanConstraints = PlanConstraints {
60    // The maximum object size that can be allocated without LOS is restricted by the max immix object size.
61    // This might be too restrictive, as our default allocator is bump pointer (nursery allocator) which
62    // can allocate objects larger than max immix object size. However, for copying, we haven't implemented
63    // copying to LOS so we always copy from nursery to the mature immix space. In this case, we should not
64    // allocate objects larger than the max immix object size to nursery as well.
65    // TODO: We may want to fix this, as this possibly has negative performance impact.
66    max_non_los_default_alloc_bytes: crate::util::rust_util::min_of_usize(
67        crate::policy::immix::MAX_IMMIX_OBJECT_SIZE,
68        crate::plan::generational::GEN_CONSTRAINTS.max_non_los_default_alloc_bytes,
69    ),
70    ..crate::plan::generational::GEN_CONSTRAINTS
71};
72
73impl<VM: VMBinding> Plan for GenImmix<VM> {
74    fn constraints(&self) -> &'static PlanConstraints {
75        &GENIMMIX_CONSTRAINTS
76    }
77
78    fn create_copy_config(&'static self) -> CopyConfig<Self::VM> {
79        use enum_map::enum_map;
80        CopyConfig {
81            copy_mapping: enum_map! {
82                CopySemantics::PromoteToMature => CopySelector::ImmixHybrid(0),
83                CopySemantics::Mature => CopySelector::ImmixHybrid(0),
84                _ => CopySelector::Unused,
85            },
86            space_mapping: vec![(CopySelector::ImmixHybrid(0), &self.immix_space)],
87            constraints: &GENIMMIX_CONSTRAINTS,
88        }
89    }
90
91    fn last_collection_was_exhaustive(&self) -> bool {
92        self.last_gc_was_full_heap.load(Ordering::Relaxed)
93            && self
94                .immix_space
95                .is_last_gc_exhaustive(self.last_gc_was_defrag.load(Ordering::Relaxed))
96    }
97
98    fn collection_required(&self, space_full: bool, space: Option<SpaceStats<Self::VM>>) -> bool
99    where
100        Self: Sized,
101    {
102        self.gen.collection_required(self, space_full, space)
103    }
104
105    fn schedule_collection(&'static self, scheduler: &GCWorkScheduler<Self::VM>) {
106        let is_full_heap = self.requires_full_heap_collection();
107        probe!(mmtk, gen_full_heap, is_full_heap);
108
109        if !is_full_heap {
110            info!("Nursery GC");
111            scheduler.schedule_common_work::<GenImmixNurseryGCWorkContext<VM>>(self);
112        } else {
113            info!("Full heap GC");
114            crate::plan::immix::Immix::schedule_immix_full_heap_collection::<
115                GenImmix<VM>,
116                GenImmixMatureGCWorkContext<VM, TRACE_KIND_FAST>,
117                GenImmixMatureGCWorkContext<VM, TRACE_KIND_DEFRAG>,
118            >(self, &self.immix_space, scheduler);
119        }
120    }
121
122    fn get_allocator_mapping(&self) -> &'static EnumMap<AllocationSemantics, AllocatorSelector> {
123        &super::mutator::ALLOCATOR_MAPPING
124    }
125
126    fn prepare(&mut self, tls: VMWorkerThread) {
127        let full_heap = !self.gen.is_current_gc_nursery();
128        self.gen.prepare(tls);
129        if full_heap {
130            self.immix_space.prepare(
131                full_heap,
132                Some(StatsForDefrag::new(self)),
133                // Bulk clear unlog bits so that we will reconstruct them.
134                UnlogBitsOperation::BulkClear,
135            );
136        } else {
137            // We don't do anything special to unlog bits during nursery GC
138            // because ProcessModBuf will set the unlog bits back.
139        }
140    }
141
142    fn release(&mut self, tls: VMWorkerThread) {
143        let full_heap = !self.gen.is_current_gc_nursery();
144        self.gen.release(tls);
145        if full_heap {
146            self.immix_space.release(
147                full_heap,
148                // We reconstructred unlog bits during tracing.  Keep them.
149                UnlogBitsOperation::NoOp,
150            );
151        } else {
152            // We don't do anything special to unlog bits during nursery GC
153            // because ProcessModBuf has set the unlog bits back.
154        }
155
156        self.last_gc_was_full_heap
157            .store(full_heap, Ordering::Relaxed);
158    }
159
160    fn end_of_pause(&mut self, mmtk: &'static MMTK<VM>, tls: VMWorkerThread) {
161        let next_gc_full_heap = CommonGenPlan::should_next_gc_be_full_heap(self);
162        self.gen.end_of_pause(tls, next_gc_full_heap);
163
164        let did_defrag = self.immix_space.end_of_gc();
165        self.last_gc_was_defrag.store(did_defrag, Ordering::Relaxed);
166
167        mmtk.gc_trigger.policy.on_gc_end(mmtk);
168    }
169
170    fn current_gc_may_move_object(&self) -> bool {
171        if self.is_current_gc_nursery() {
172            true
173        } else {
174            self.immix_space.in_defrag()
175        }
176    }
177
178    fn get_collection_reserved_pages(&self) -> usize {
179        self.gen.get_collection_reserved_pages() + self.immix_space.defrag_headroom_pages()
180    }
181
182    fn get_used_pages(&self) -> usize {
183        self.gen.get_used_pages() + self.immix_space.reserved_pages()
184    }
185
186    /// Return the number of pages available for allocation. Assuming all future allocations goes to nursery.
187    fn get_available_pages(&self) -> usize {
188        // super.get_available_pages() / 2 to reserve pages for copying
189        (self
190            .get_total_pages()
191            .saturating_sub(self.get_reserved_pages()))
192            >> 1
193    }
194
195    fn base(&self) -> &BasePlan<VM> {
196        &self.gen.common.base
197    }
198
199    fn base_mut(&mut self) -> &mut BasePlan<Self::VM> {
200        &mut self.gen.common.base
201    }
202
203    fn common(&self) -> &CommonPlan<VM> {
204        &self.gen.common
205    }
206
207    fn generational(&self) -> Option<&dyn GenerationalPlan<VM = VM>> {
208        Some(self)
209    }
210}
211
212impl<VM: VMBinding> GenerationalPlan for GenImmix<VM> {
213    fn is_current_gc_nursery(&self) -> bool {
214        self.gen.is_current_gc_nursery()
215    }
216
217    fn is_object_in_nursery(&self, object: ObjectReference) -> bool {
218        self.gen.nursery.in_space(object)
219    }
220
221    fn is_address_in_nursery(&self, addr: Address) -> bool {
222        self.gen.nursery.address_in_space(addr)
223    }
224
225    fn get_mature_physical_pages_available(&self) -> usize {
226        self.immix_space.available_physical_pages()
227    }
228
229    fn get_mature_reserved_pages(&self) -> usize {
230        self.immix_space.reserved_pages()
231    }
232
233    fn force_full_heap_collection(&self) {
234        self.gen.force_full_heap_collection()
235    }
236
237    fn last_collection_full_heap(&self) -> bool {
238        self.gen.last_collection_full_heap()
239    }
240}
241
242impl<VM: VMBinding> crate::plan::generational::global::GenerationalPlanExt<VM> for GenImmix<VM> {
243    fn trace_object_nursery<Q: ObjectQueue, const KIND: TraceKind>(
244        &self,
245        queue: &mut Q,
246        object: ObjectReference,
247        worker: &mut GCWorker<VM>,
248    ) -> ObjectReference {
249        self.gen
250            .trace_object_nursery::<Q, KIND>(queue, object, worker)
251    }
252}
253
254impl<VM: VMBinding> GenImmix<VM> {
255    pub fn new(args: CreateGeneralPlanArgs<VM>) -> Self {
256        let mut plan_args = CreateSpecificPlanArgs {
257            global_args: args,
258            constraints: &GENIMMIX_CONSTRAINTS,
259            global_side_metadata_specs:
260                crate::plan::generational::new_generational_global_metadata_specs::<VM>(),
261        };
262        let immix_space = ImmixSpace::new(
263            plan_args.get_mature_space_args(
264                "immix_mature",
265                true,
266                false,
267                VMRequest::discontiguous(),
268            ),
269            ImmixSpaceArgs {
270                // In GenImmix, young objects are not allocated in ImmixSpace directly.
271                mixed_age: false,
272                never_move_objects: false,
273            },
274        );
275
276        GenImmix {
277            gen: CommonGenPlan::new(plan_args),
278            immix_space,
279            last_gc_was_defrag: AtomicBool::new(false),
280            last_gc_was_full_heap: AtomicBool::new(false),
281        }
282    }
283
284    fn requires_full_heap_collection(&self) -> bool {
285        self.gen.requires_full_heap_collection(self)
286    }
287}