mmtk/plan/immix/
global.rs

1use super::gc_work::ImmixGCWorkContext;
2use super::mutator::ALLOCATOR_MAPPING;
3use crate::plan::global::BasePlan;
4use crate::plan::global::CommonPlan;
5use crate::plan::global::CreateGeneralPlanArgs;
6use crate::plan::global::CreateSpecificPlanArgs;
7use crate::plan::AllocationSemantics;
8use crate::plan::Plan;
9use crate::plan::PlanConstraints;
10use crate::policy::immix::ImmixSpaceArgs;
11use crate::policy::immix::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST};
12use crate::policy::space::Space;
13use crate::scheduler::*;
14use crate::util::alloc::allocators::AllocatorSelector;
15use crate::util::copy::*;
16use crate::util::heap::gc_trigger::SpaceStats;
17use crate::util::heap::VMRequest;
18use crate::util::metadata::log_bit::UnlogBitsOperation;
19use crate::util::metadata::side_metadata::SideMetadataContext;
20use crate::vm::VMBinding;
21use crate::MMTK;
22use crate::{policy::immix::ImmixSpace, util::opaque_pointer::VMWorkerThread};
23use std::sync::atomic::AtomicBool;
24
25use atomic::Ordering;
26use enum_map::EnumMap;
27
28use mmtk_macros::{HasSpaces, PlanTraceObject};
29
30#[derive(HasSpaces, PlanTraceObject)]
31pub struct Immix<VM: VMBinding> {
32    #[post_scan]
33    #[space]
34    #[copy_semantics(CopySemantics::DefaultCopy)]
35    pub immix_space: ImmixSpace<VM>,
36    #[parent]
37    pub common: CommonPlan<VM>,
38    last_gc_was_defrag: AtomicBool,
39}
40
41/// The plan constraints for the immix plan.
42pub const IMMIX_CONSTRAINTS: PlanConstraints = PlanConstraints {
43    // If we disable moving in Immix, this is a non-moving plan.
44    moves_objects: !cfg!(feature = "immix_non_moving"),
45    // Max immix object size is half of a block.
46    max_non_los_default_alloc_bytes: crate::policy::immix::MAX_IMMIX_OBJECT_SIZE,
47    ..PlanConstraints::default()
48};
49
50impl<VM: VMBinding> Plan for Immix<VM> {
51    fn collection_required(&self, space_full: bool, _space: Option<SpaceStats<Self::VM>>) -> bool {
52        self.base().collection_required(self, space_full)
53    }
54
55    fn last_collection_was_exhaustive(&self) -> bool {
56        self.immix_space
57            .is_last_gc_exhaustive(self.last_gc_was_defrag.load(Ordering::Relaxed))
58    }
59
60    fn constraints(&self) -> &'static PlanConstraints {
61        &IMMIX_CONSTRAINTS
62    }
63
64    fn create_copy_config(&'static self) -> CopyConfig<Self::VM> {
65        use enum_map::enum_map;
66        CopyConfig {
67            copy_mapping: enum_map! {
68                CopySemantics::DefaultCopy => CopySelector::Immix(0),
69                _ => CopySelector::Unused,
70            },
71            space_mapping: vec![(CopySelector::Immix(0), &self.immix_space)],
72            constraints: &IMMIX_CONSTRAINTS,
73        }
74    }
75
76    fn schedule_collection(&'static self, scheduler: &GCWorkScheduler<VM>) {
77        Self::schedule_immix_full_heap_collection::<
78            Immix<VM>,
79            ImmixGCWorkContext<VM, TRACE_KIND_FAST>,
80            ImmixGCWorkContext<VM, TRACE_KIND_DEFRAG>,
81        >(self, &self.immix_space, scheduler)
82    }
83
84    fn get_allocator_mapping(&self) -> &'static EnumMap<AllocationSemantics, AllocatorSelector> {
85        &ALLOCATOR_MAPPING
86    }
87
88    fn prepare(&mut self, tls: VMWorkerThread) {
89        self.prepare_inner(tls, UnlogBitsOperation::NoOp)
90    }
91
92    fn release(&mut self, tls: VMWorkerThread) {
93        self.release_inner(tls, UnlogBitsOperation::NoOp);
94    }
95
96    fn end_of_pause(&mut self, mmtk: &'static MMTK<VM>, tls: VMWorkerThread) {
97        self.last_gc_was_defrag
98            .store(self.immix_space.end_of_gc(), Ordering::Relaxed);
99        self.common.end_of_pause(tls);
100        mmtk.gc_trigger.policy.on_gc_end(mmtk);
101    }
102
103    fn current_gc_may_move_object(&self) -> bool {
104        self.immix_space.in_defrag()
105    }
106
107    fn get_collection_reserved_pages(&self) -> usize {
108        self.immix_space.defrag_headroom_pages()
109    }
110
111    fn get_used_pages(&self) -> usize {
112        self.immix_space.reserved_pages() + self.common.get_used_pages()
113    }
114
115    fn base(&self) -> &BasePlan<VM> {
116        &self.common.base
117    }
118
119    fn base_mut(&mut self) -> &mut BasePlan<Self::VM> {
120        &mut self.common.base
121    }
122
123    fn common(&self) -> &CommonPlan<VM> {
124        &self.common
125    }
126}
127
128impl<VM: VMBinding> Immix<VM> {
129    pub fn new(args: CreateGeneralPlanArgs<VM>) -> Self {
130        let plan_args = CreateSpecificPlanArgs {
131            global_args: args,
132            constraints: &IMMIX_CONSTRAINTS,
133            global_side_metadata_specs: SideMetadataContext::new_global_specs(&[]),
134        };
135        Self::new_with_args(
136            plan_args,
137            ImmixSpaceArgs {
138                mixed_age: false,
139                never_move_objects: false,
140            },
141        )
142    }
143
144    pub fn new_with_args(
145        mut plan_args: CreateSpecificPlanArgs<VM>,
146        space_args: ImmixSpaceArgs,
147    ) -> Self {
148        Immix {
149            immix_space: ImmixSpace::new(
150                if space_args.mixed_age {
151                    plan_args.get_mixed_age_space_args(
152                        "immix",
153                        true,
154                        false,
155                        VMRequest::discontiguous(),
156                    )
157                } else {
158                    plan_args.get_normal_space_args(
159                        "immix",
160                        true,
161                        false,
162                        VMRequest::discontiguous(),
163                    )
164                },
165                space_args,
166            ),
167            common: CommonPlan::new(plan_args),
168            last_gc_was_defrag: AtomicBool::new(false),
169        }
170    }
171
172    /// Schedule a full heap immix collection. This method is used by immix/genimmix/stickyimmix
173    /// to schedule a full heap collection. A plan must call set_collection_kind and set_gc_status before this method.
174    pub(crate) fn schedule_immix_full_heap_collection<
175        PlanType: Plan<VM = VM>,
176        FastContext: GCWorkContext<VM = VM, PlanType = PlanType>,
177        DefragContext: GCWorkContext<VM = VM, PlanType = PlanType>,
178    >(
179        plan: &'static DefragContext::PlanType,
180        immix_space: &ImmixSpace<VM>,
181        scheduler: &GCWorkScheduler<VM>,
182    ) {
183        let in_defrag = immix_space.decide_whether_to_defrag(
184            plan.base().global_state.is_emergency_collection(),
185            true,
186            plan.base()
187                .global_state
188                .cur_collection_attempts
189                .load(Ordering::SeqCst),
190            plan.base().global_state.is_user_triggered_collection(),
191            *plan.base().options.full_heap_system_gc,
192        );
193
194        if in_defrag {
195            scheduler.schedule_common_work::<DefragContext>(plan);
196        } else {
197            scheduler.schedule_common_work::<FastContext>(plan);
198        }
199    }
200
201    pub(in crate::plan) fn set_last_gc_was_defrag(&self, defrag: bool, order: Ordering) {
202        self.last_gc_was_defrag.store(defrag, order)
203    }
204
205    /// Prepare with unlog-bit operation.
206    /// Some Immix-derived plans may need to set/clear unlog bits when preparing.
207    pub(in crate::plan) fn prepare_inner(
208        &mut self,
209        tls: VMWorkerThread,
210        unlog_bits_op: UnlogBitsOperation,
211    ) {
212        self.common.prepare(tls, true);
213        self.immix_space.prepare(
214            true,
215            Some(crate::policy::immix::defrag::StatsForDefrag::new(self)),
216            unlog_bits_op,
217        );
218    }
219
220    /// Release with unlog-bit operation.
221    /// Some Immix-derived plans may need to set/clear unlog bits when releasing.
222    pub(in crate::plan) fn release_inner(
223        &mut self,
224        tls: VMWorkerThread,
225        unlog_bits_op: UnlogBitsOperation,
226    ) {
227        self.common.release(tls, true);
228        // release the collected region
229        self.immix_space.release(true, unlog_bits_op);
230    }
231}