mmtk/plan/lxr/gc_work/
mature_evac.rs

1use 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        // Iterate over all blocks in this chunk
40        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                // Skip unallocated blocks.
53                if MatureEvacuationSet::skip_block(block) {
54                    continue;
55                }
56                // This is a fragmented block?
57                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        // Flush to global fragmented_blocks
64        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        // Keep slots not in the mmtk heap
96        // These should be slots in the c++ `ClassLoaderData` objects. We remember these slots
97        // in the remembered-set to avoid expensive CLD scanning.
98        let addr = s.to_address();
99        // Check reuse count
100        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        // Skip slots in collection set
114        if lxr.address_in_defrag(addr) {
115            return false;
116        }
117        // Check if it is a real oop field
118        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        // Skip slots that does not contain a real oop
129        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        // transitive closure
171        worker.add_boxed_work(WorkBucketStage::Closure, work)
172    }
173}