mmtk/plan/generational/
global.rs1use crate::plan::global::CommonPlan;
2use crate::plan::global::CreateSpecificPlanArgs;
3use crate::plan::ObjectQueue;
4use crate::plan::Plan;
5use crate::policy::copyspace::CopySpace;
6use crate::policy::gc_work::{TraceKind, TRACE_KIND_TRANSITIVE_PIN};
7use crate::policy::space::Space;
8use crate::scheduler::*;
9use crate::util::copy::CopySemantics;
10use crate::util::heap::gc_trigger::SpaceStats;
11use crate::util::heap::VMRequest;
12use crate::util::statistics::counter::EventCounter;
13use crate::util::Address;
14use crate::util::ObjectReference;
15use crate::util::VMWorkerThread;
16use crate::vm::{ObjectModel, VMBinding};
17use std::sync::atomic::AtomicBool;
18use std::sync::atomic::Ordering;
19use std::sync::{Arc, Mutex};
20
21use mmtk_macros::{HasSpaces, PlanTraceObject};
22
23#[derive(HasSpaces, PlanTraceObject)]
26pub struct CommonGenPlan<VM: VMBinding> {
27 #[space]
29 #[copy_semantics(CopySemantics::PromoteToMature)]
30 pub nursery: CopySpace<VM>,
31 #[parent]
33 pub common: CommonPlan<VM>,
34 pub gc_full_heap: AtomicBool,
36 pub next_gc_full_heap: AtomicBool,
38 pub full_heap_gc_count: Arc<Mutex<EventCounter>>,
39}
40
41impl<VM: VMBinding> CommonGenPlan<VM> {
42 pub fn new(mut args: CreateSpecificPlanArgs<VM>) -> Self {
43 let nursery = CopySpace::new(
44 args.get_nursery_space_args("nursery", true, false, VMRequest::discontiguous()),
45 true,
46 );
47 let full_heap_gc_count = args
48 .global_args
49 .stats
50 .new_event_counter("majorGC", true, true);
51 let common = CommonPlan::new(args);
52
53 CommonGenPlan {
54 nursery,
55 common,
56 gc_full_heap: AtomicBool::default(),
57 next_gc_full_heap: AtomicBool::new(false),
58 full_heap_gc_count,
59 }
60 }
61
62 pub fn prepare(&mut self, tls: VMWorkerThread) {
64 let full_heap = !self.is_current_gc_nursery();
65
66 if full_heap {
69 self.full_heap_gc_count.lock().unwrap().inc();
70 self.common.prepare(tls, full_heap);
71 } else {
72 self.common.los.prepare(full_heap);
73 }
74
75 self.nursery.prepare(true);
76 self.nursery
77 .set_copy_for_sft_trace(Some(CopySemantics::PromoteToMature));
78 }
79
80 pub fn release(&mut self, tls: VMWorkerThread) {
82 let full_heap = !self.is_current_gc_nursery();
83
84 if full_heap {
87 self.common.release(tls, full_heap);
88 } else {
89 self.common.los.release(full_heap);
90 }
91 self.nursery.release();
92 }
93
94 pub fn on_pause_end(&mut self, tls: VMWorkerThread, next_gc_full_heap: bool) {
95 self.set_next_gc_full_heap(next_gc_full_heap);
96 self.common.on_pause_end(tls);
97 }
98
99 fn virtual_memory_exhausted(plan: &dyn GenerationalPlan<VM = VM>) -> bool {
106 ((plan.get_collection_reserved_pages() as f64
107 * VM::VMObjectModel::VM_WORST_CASE_COPY_EXPANSION) as usize)
108 > plan.get_mature_physical_pages_available()
109 }
110
111 pub fn collection_required<P: Plan<VM = VM>>(
114 &self,
115 plan: &P,
116 space_full: bool,
117 space: Option<SpaceStats<VM>>,
118 ) -> bool {
119 let cur_nursery = self.nursery.reserved_pages();
120 let max_nursery = self.common.base.gc_trigger.get_max_nursery_pages();
121 let nursery_full = cur_nursery >= max_nursery;
122 trace!(
123 "nursery_full = {:?} (nursery = {}, max_nursery = {})",
124 nursery_full,
125 cur_nursery,
126 max_nursery,
127 );
128 if nursery_full {
129 return true;
130 }
131 if Self::virtual_memory_exhausted(plan.generational().unwrap()) {
132 return true;
133 }
134
135 let is_triggered_by_nursery =
139 space.is_some_and(|s| s.0.common().descriptor == self.nursery.common().descriptor);
140 if space_full && !is_triggered_by_nursery {
142 self.next_gc_full_heap.store(true, Ordering::SeqCst);
143 }
144
145 self.common.base.collection_required(plan, space_full)
146 }
147
148 pub fn force_full_heap_collection(&self) {
149 self.next_gc_full_heap.store(true, Ordering::SeqCst);
150 }
151
152 pub fn last_collection_full_heap(&self) -> bool {
153 self.gc_full_heap.load(Ordering::Relaxed)
154 }
155
156 pub fn requires_full_heap_collection<P: Plan<VM = VM>>(&self, plan: &P) -> bool {
159 #[allow(clippy::if_same_then_else, clippy::needless_bool)]
162 let is_full_heap = if crate::plan::generational::FULL_NURSERY_GC {
163 trace!("full heap: forced full heap");
164 true
166 } else if self
167 .common
168 .base
169 .global_state
170 .user_triggered_collection
171 .load(Ordering::SeqCst)
172 && *self.common.base.options.full_heap_system_gc
173 {
174 trace!("full heap: user triggered");
175 true
177 } else if self.next_gc_full_heap.load(Ordering::SeqCst)
178 || self
179 .common
180 .base
181 .global_state
182 .cur_collection_attempts
183 .load(Ordering::SeqCst)
184 > 1
185 {
186 trace!(
187 "full heap: next_gc_full_heap = {}, cur_collection_attempts = {}",
188 self.next_gc_full_heap.load(Ordering::SeqCst),
189 self.common
190 .base
191 .global_state
192 .cur_collection_attempts
193 .load(Ordering::SeqCst)
194 );
195 true
197 } else if Self::virtual_memory_exhausted(plan.generational().unwrap()) {
198 trace!("full heap: virtual memory exhausted");
199 true
200 } else {
201 false
207 };
208
209 self.gc_full_heap.store(is_full_heap, Ordering::SeqCst);
210
211 info!(
212 "{}",
213 if is_full_heap {
214 "Full heap GC"
215 } else {
216 "Nursery GC"
217 }
218 );
219
220 is_full_heap
221 }
222
223 pub fn trace_object_nursery<Q: ObjectQueue, const KIND: TraceKind>(
225 &self,
226 queue: &mut Q,
227 object: ObjectReference,
228 worker: &mut GCWorker<VM>,
229 ) -> ObjectReference {
230 assert!(
231 KIND != TRACE_KIND_TRANSITIVE_PIN,
232 "A copying nursery cannot pin objects"
233 );
234
235 if self.nursery.in_space(object) {
237 return self.nursery.trace_object::<Q>(
238 queue,
239 object,
240 Some(CopySemantics::PromoteToMature),
241 worker,
242 );
243 }
244 if self.common.get_los().in_space(object) {
246 return self.common.get_los().trace_object::<Q>(queue, object);
247 }
248
249 object
250 }
251
252 pub fn is_current_gc_nursery(&self) -> bool {
254 !self.gc_full_heap.load(Ordering::SeqCst)
255 }
256
257 pub fn should_next_gc_be_full_heap(plan: &dyn Plan<VM = VM>) -> bool {
264 let available = plan.get_available_pages();
265 let min_nursery = plan.base().gc_trigger.get_min_nursery_pages();
266 let next_gc_full_heap = available < min_nursery;
267 trace!(
268 "next gc will be full heap? {}, available pages = {}, min nursery = {}",
269 next_gc_full_heap,
270 available,
271 min_nursery
272 );
273 next_gc_full_heap
274 }
275
276 pub fn set_next_gc_full_heap(&self, next_gc_full_heap: bool) {
278 self.next_gc_full_heap
279 .store(next_gc_full_heap, Ordering::SeqCst);
280 }
281
282 pub fn get_collection_reserved_pages(&self) -> usize {
285 self.nursery.reserved_pages()
286 }
287
288 pub fn get_used_pages(&self) -> usize {
291 self.nursery.reserved_pages() + self.common.get_used_pages()
292 }
293}
294
295pub trait GenerationalPlan: Plan {
298 fn is_current_gc_nursery(&self) -> bool;
301
302 fn is_object_in_nursery(&self, object: ObjectReference) -> bool;
304
305 fn is_address_in_nursery(&self, addr: Address) -> bool;
311
312 fn get_mature_physical_pages_available(&self) -> usize;
314
315 fn get_mature_reserved_pages(&self) -> usize;
317
318 fn last_collection_full_heap(&self) -> bool;
320
321 fn force_full_heap_collection(&self);
323}
324
325pub trait GenerationalPlanExt<VM: VMBinding>: GenerationalPlan<VM = VM> {
329 fn trace_object_nursery<Q: ObjectQueue, const KIND: TraceKind>(
332 &self,
333 queue: &mut Q,
334 object: ObjectReference,
335 worker: &mut GCWorker<VM>,
336 ) -> ObjectReference;
337}
338
339pub fn is_nursery_gc<VM: VMBinding>(plan: &dyn Plan<VM = VM>) -> bool {
342 plan.generational()
343 .is_some_and(|plan| plan.is_current_gc_nursery())
344}