mmtk/plan/lxr/gc_work/
mature_evac.rs1use std::marker::PhantomData;
2use std::ops::Range;
3use std::sync::atomic::{AtomicUsize, Ordering};
4
5use super::tracing::LXRStopTheWorldProcessEdges;
6use crate::plan::lxr::mature_evac::MatureEvacuationSet;
7use crate::policy::immix::line::Line;
8use crate::util::heap::chunk_map::Chunk;
9use crate::util::linear_scan::Region;
10use crate::util::metadata::side_metadata::spec_defs::{IX_LINE_REUSE_COUNT, LOS_PAGE_REUSE_COUNT};
11use crate::vm::slot::Slot;
12use crate::{
13 plan::concurrent::Pause,
14 policy::{
15 immix::block::{Block, BlockState},
16 space::Space,
17 },
18 scheduler::{GCWork, GCWorker, WorkBucketStage},
19 vm::VMBinding,
20 MMTK,
21};
22
23use super::super::mature_evac::RemSetEntry;
24use super::super::LXR;
25
26pub static SELECT_DEFRAG_BLOCK_JOB_COUNTER: AtomicUsize = AtomicUsize::new(0);
27
28pub struct SelectDefragBlocks {
29 pub chunks: Range<Chunk>,
30 #[allow(unused)]
31 pub defrag_threshold: usize,
32}
33
34impl<VM: VMBinding> GCWork<VM> for SelectDefragBlocks {
35 fn do_work(&mut self, _worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
36 let mut fragmented_blocks = vec![];
37 let lxr = mmtk.get_plan().downcast_ref::<LXR<VM>>().unwrap();
38
39 let num_chunks = (self.chunks.end.start() - self.chunks.start.start()) >> Chunk::LOG_BYTES;
41 let ix_space = &mmtk
42 .get_plan()
43 .downcast_ref::<LXR<VM>>()
44 .unwrap()
45 .immix_space;
46 for i in 0..num_chunks {
47 let chunk = self.chunks.start.next_nth(i);
48 if !ix_space.chunk_map.is_allocated(chunk) {
49 continue;
50 }
51 for block in chunk.iter_region::<Block>() {
52 if MatureEvacuationSet::skip_block(block) {
54 continue;
55 }
56 let score = block.calc_dead_lines() << Line::LOG_BYTES;
58 if lxr.current_pause().unwrap() == Pause::Full || score >= (Block::BYTES >> 1) {
59 fragmented_blocks.push((block, score));
60 }
61 }
62 }
63 if !fragmented_blocks.is_empty() {
65 lxr.evac_set
66 .fragmented_blocks_size
67 .fetch_add(fragmented_blocks.len(), Ordering::SeqCst);
68 lxr.evac_set.fragmented_blocks.push(fragmented_blocks);
69 }
70
71 if SELECT_DEFRAG_BLOCK_JOB_COUNTER.fetch_sub(1, Ordering::SeqCst) == 1 {
72 lxr.evac_set.select_mature_evacuation_candidates(lxr)
73 }
74 }
75}
76
77pub struct EvacuateMatureObjects<VM: VMBinding> {
78 remset: Vec<RemSetEntry<VM>>,
79 _p: PhantomData<VM>,
80}
81
82impl<VM: VMBinding> EvacuateMatureObjects<VM> {
83 pub const CAPACITY: usize = 1024;
84
85 #[allow(clippy::assertions_on_constants)]
86 pub fn new(remset: Vec<RemSetEntry<VM>>) -> Self {
87 debug_assert!(super::super::MATURE_EVACUATION);
88 Self {
89 remset,
90 _p: PhantomData,
91 }
92 }
93
94 fn address_is_valid_oop_slot(&self, s: VM::VMSlot, original_reuse: u8, lxr: &LXR<VM>) -> bool {
95 let addr = s.to_address();
99 if lxr.immix_space.address_in_space(addr) {
101 let reuse = IX_LINE_REUSE_COUNT.load_atomic::<u8>(addr, atomic::Ordering::SeqCst);
102 if reuse != original_reuse {
103 return false;
104 }
105 } else if lxr.los().address_in_space(addr) {
106 let reuse = LOS_PAGE_REUSE_COUNT.load_atomic::<u8>(addr, atomic::Ordering::SeqCst);
107 if reuse != original_reuse {
108 return false;
109 }
110 } else {
111 return false;
112 }
113 if lxr.address_in_defrag(addr) {
115 return false;
116 }
117 if lxr.immix_space.address_in_space(s.to_address()) {
119 let block = Block::from_unaligned_address(s.to_address());
120 if block.get_state() == BlockState::Unallocated {
121 return false;
122 }
123 }
124 true
125 }
126
127 fn process_slot(&mut self, s: VM::VMSlot, reuse: u8, lxr: &LXR<VM>) -> bool {
128 if !self.address_is_valid_oop_slot(s, reuse, lxr) {
130 return false;
131 }
132 let Some(o) = s.load() else {
133 return false;
134 };
135 if !o.is_in_any_space() || !lxr.immix_space.in_space(o) {
136 return false;
137 }
138 if !lxr.rc.is_dead(o) && Block::in_defrag_block(o) {
139 return true;
140 }
141 false
142 }
143
144 fn process_slots(&mut self, mmtk: &'static MMTK<VM>) -> Option<Box<dyn GCWork<VM>>> {
145 let lxr = mmtk.get_plan().downcast_ref::<LXR<VM>>().unwrap();
146 assert_eq!(lxr.current_pause(), Some(Pause::FinalMark));
147 let remset = std::mem::take(&mut self.remset);
148 let mut slots = vec![];
149 for entry in remset {
150 let (s, reuse) = entry.decode();
151 if self.process_slot(s, reuse, lxr) {
152 slots.push(s);
153 }
154 }
155 if !slots.is_empty() {
156 Some(Box::new(
157 LXRStopTheWorldProcessEdges::<_, false>::new_remset(slots, mmtk),
158 ))
159 } else {
160 None
161 }
162 }
163}
164
165impl<VM: VMBinding> GCWork<VM> for EvacuateMatureObjects<VM> {
166 fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
167 let Some(work) = self.process_slots(mmtk) else {
168 return;
169 };
170 worker.add_boxed_work(WorkBucketStage::Closure, work)
172 }
173}