Skip to main content

arc_gc/
gc.rs

1use std::{
2    collections::VecDeque,
3    sync::{atomic::AtomicUsize, Mutex},
4};
5
6use rustc_hash::FxHashMap;
7
8use crate::{
9    arc::{GCArc, GCRef},
10    traceable::GCTraceable,
11};
12
13pub struct GC<T: GCTraceable<T> + 'static> {
14    gc_refs: Mutex<Vec<GCArc<T>>>,
15    attach_count: AtomicUsize,
16    collection_percentage: usize, // 百分比阈值,如20表示20%
17    memory_threshold: Option<usize>, // 内存阈值(字节),达到此值时触发回收
18    allocated_memory: AtomicUsize, // 当前分配的内存大小估算
19}
20
21#[allow(dead_code)]
22impl<T> GC<T>
23where
24    T: GCTraceable<T> + 'static,
25{    /// 创建一个新的垃圾回收器,默认回收触发百分比为20%
26    pub fn new() -> Self {
27        Self {
28            gc_refs: Mutex::new(Vec::new()),
29            attach_count: AtomicUsize::new(0),
30            collection_percentage: 20, // 默认20%增长时触发回收
31            memory_threshold: None, // 默认不使用内存阈值
32            allocated_memory: AtomicUsize::new(0),
33        }
34    }    /// 创建一个新的垃圾回收器,指定回收触发的百分比
35    /// 例如,`new_with_percentage(30)`表示当attach次数超过当前对象数的30%时触发回收
36    pub fn new_with_percentage(percentage: usize) -> Self {
37        Self {
38            gc_refs: Mutex::new(Vec::new()),
39            attach_count: AtomicUsize::new(0),
40            collection_percentage: percentage,
41            memory_threshold: None, // 默认不使用内存阈值
42            allocated_memory: AtomicUsize::new(0),
43        }
44    }
45
46    /// 创建一个新的垃圾回收器,指定内存阈值(字节)
47    /// 当分配的内存超过指定阈值时触发回收
48    pub fn new_with_memory_threshold(memory_threshold: usize) -> Self {
49        Self {
50            gc_refs: Mutex::new(Vec::new()),
51            attach_count: AtomicUsize::new(0),
52            collection_percentage: 20, // 保持默认百分比作为备用触发条件
53            memory_threshold: Some(memory_threshold),
54            allocated_memory: AtomicUsize::new(0),
55        }
56    }
57
58    /// 创建一个新的垃圾回收器,同时指定百分比阈值和内存阈值
59    /// 任一条件满足时都会触发回收
60    pub fn new_with_thresholds(percentage: usize, memory_threshold: usize) -> Self {
61        Self {
62            gc_refs: Mutex::new(Vec::new()),
63            attach_count: AtomicUsize::new(0),
64            collection_percentage: percentage,
65            memory_threshold: Some(memory_threshold),
66            allocated_memory: AtomicUsize::new(0),
67        }
68    }    pub fn attach(&mut self, gc_arc: &GCArc<T>) {
69        {
70            let mut gc_refs = self.gc_refs.lock().unwrap();
71            gc_refs.push(gc_arc.clone());
72        }
73
74        self.attach_count
75            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
76
77        gc_arc
78            .inner()
79            .attached_gc_count
80            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
81
82        // 更新内存估算(使用对象的大小估算)
83        let obj_size = std::mem::size_of::<T>() + std::mem::size_of::<GCArc<T>>();
84        self.allocated_memory
85            .fetch_add(obj_size, std::sync::atomic::Ordering::Relaxed);
86
87        // 启发式回收检查
88        if self.should_collect() {
89            self.collect();
90        }
91    }    pub fn detach(&mut self, gc_arc: &GCArc<T>) -> bool {
92        let mut gc_refs = self.gc_refs.lock().unwrap();
93        if let Some(index) = gc_refs.iter().position(|r| GCArc::ptr_eq(r, gc_arc)) {
94            gc_refs.swap_remove(index);
95            gc_arc
96                .inner()
97                .attached_gc_count
98                .fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
99            
100            // 更新内存估算
101            let obj_size = std::mem::size_of::<T>() + std::mem::size_of::<GCArc<T>>();
102            self.allocated_memory
103                .fetch_sub(obj_size, std::sync::atomic::Ordering::Relaxed);
104            
105            true
106        } else {
107            false
108        }
109    }
110    pub fn collect(&mut self) {
111        // 执行垃圾回收过程。
112        // 该过程分为两个主要阶段:标记(Mark)和清除(Sweep)。
113        // 1. 标记阶段:从根对象开始,遍历所有可达的对象,并将其标记为“存活”。
114        // 2. 清除阶段:遍历所有GC管理的对象,回收所有未被标记为“存活”的对象。
115
116        // 获取对GC管理的引用列表的可变借用。
117        // `refs` 存储了所有由GC跟踪的 GCArc<T> 对象。
118        let mut refs = self.gc_refs.lock().unwrap();
119
120        // 初始化一个哈希表 `marked` 用于存储每个对象的标记状态。
121        // 键是对象的内存地址(usize类型),值是布尔类型(true表示已标记,false表示未标记)。
122        // 使用 FxHashMap 是为了更快的哈希性能。
123        let mut marked = FxHashMap::default();
124
125        // 初始化标记阶段:将所有GC跟踪的对象在 `marked` 表中初始标记为 `false`(未存活)。
126        // 这一步确保了在开始遍历之前,所有对象都被认为是不可达的。
127        for r in refs.iter() {
128            // 将对象的裸指针(内存地址)作为键。
129            marked.insert(r.as_ref() as *const T as usize, false);
130        }
131
132        // 初始化一个双端队列 `queue`,用于广度优先搜索(BFS)遍历对象图。
133        // 队列中存储的是对象的弱引用 (GCArcWeak<T>),以避免在遍历过程中增加强引用计数,
134        // 从而干扰对象的实际存活状态判断。
135        let mut queue = VecDeque::new();
136
137        // 识别根对象(Root Objects)。
138        // 根对象是那些除了GC自身持有的引用外,仍然有外部强引用的对象。
139        // 在这个实现中,如果一个 GCArc<T> 的强引用计数大于attached_gc_count,
140        // (其中attached_gc_count个引用来自各gc的 `gc_refs` 向量,其余来自外部代码),
141        // 则认为它是根对象。
142        // 将所有根对象的弱引用添加到处理队列 `queue` 中。
143        for r in refs.iter() {
144            if r.strong_ref()
145                > r.inner()
146                    .attached_gc_count
147                    .load(std::sync::atomic::Ordering::Relaxed)
148            {
149                // 当强引用计数大于 `attached_gc_count` 时,说明 GC 堆外存在对象(比如VM栈或其他 GCArc 的引用)则认为其为根对象
150                queue.push_back(r.as_weak());
151            }
152        }
153
154        // 开始标记阶段的遍历。
155        // 当队列不为空时,持续处理队列中的对象。
156        while !queue.is_empty() {
157            // 从队列前端取出一个弱引用。
158            // `unwrap()` 在这里是安全的,因为我们刚检查了 `!queue.is_empty()`。
159            let current_weak = queue.pop_front().unwrap();
160
161            // 尝试将弱引用升级为强引用。
162            // 如果升级失败(返回 `None`),意味着该对象已经被释放,
163            // 或者在加入队列后、处理前其强引用计数变为0,所以跳过它。
164            let Some(current_strong) = current_weak.upgrade() else {
165                continue; // 对象已被释放或不再可达
166            };
167
168            // 获取当前强引用指向对象的内存地址。
169            let current_ptr = current_strong.as_ref() as *const T as usize;
170
171            // 检查该对象是否已经被标记过。
172            // `unwrap_or(&false)` 处理了理论上不应发生的情况(对象不在 `marked` 中),
173            // 或者对象已在 `marked` 中且值为`true`。
174            // 如果对象已经被标记(即 `marked.get(&current_ptr)` 返回 `Some(&true)`),
175            // 则跳过,以避免重复处理和循环引用导致的无限循环。
176            if *marked.get(&current_ptr).unwrap_or(&false) {
177                continue; // 对象已经被访问和标记过了
178            }
179
180            // 将当前对象标记为“存活”(设置为 `true`)。
181            marked.insert(current_ptr, true);
182
183            // 访问当前对象,并收集它引用的其他GC管理的对象。
184            // `GCTraceable::collect` 方法负责将当前对象内部引用的其他
185            // `GCArcWeak<T>` 添加到 `queue` 中,以便后续处理。
186            current_strong.as_ref().collect(&mut queue);
187        }        // 清除阶段(Sweep Phase)。
188        // 根据 `marked` 表中的标记状态,筛选出所有存活的对象。
189        // `retained` 向量将只包含那些在标记阶段被标记为 `true` 的对象。
190        let retained: Vec<GCArc<T>> = refs
191            .iter()
192            .filter(|r| {
193                let ptr = r.as_ref() as *const T as usize;
194                // 如果对象在 `marked` 表中为 `true`,则保留它。
195                // `unwrap_or(&false)` 确保如果对象由于某种原因不在 `marked` 中(不应发生),
196                // 它将被视为未标记,从而被回收。
197                let retain = *marked.get(&ptr).unwrap_or(&false);
198                if !retain {
199                    // 如果对象未被标记为存活,则减少持有的 GC 实例数,因为其将被立即移出堆
200                    r.inner()
201                        .attached_gc_count
202                        .fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
203                    
204                    // 从内存计数中减去被回收对象的大小
205                    let obj_size = std::mem::size_of::<T>() + std::mem::size_of::<GCArc<T>>();
206                    self.allocated_memory
207                        .fetch_sub(obj_size, std::sync::atomic::Ordering::Relaxed);
208                }
209                retain
210            })
211            .cloned() // 克隆 GCArc<T> 以便在新向量中拥有它们的所有权。
212            .collect();
213
214        // 清空旧的 `refs` 列表。
215        refs.clear();
216        // 将所有存活的对象添加回 `refs` 列表。
217        // 此时,`refs` 只包含标记阶段确认存活的对象。
218        // 那些未被标记的对象(即 `retained` 中没有的对象)的 `GCArc` 将会在这里被丢弃。
219        // 如果这些是最后的强引用,对象本身将被 `Drop`。
220        refs.extend(retained);
221
222        // 重置 `attach_count` 计数器。
223        // `attach_count` 用于启发式地决定何时运行垃圾回收。
224        // 在一次完整的回收之后,这个计数器被重置为0。
225        self.attach_count
226            .store(0, std::sync::atomic::Ordering::Relaxed);
227    }
228    pub fn object_count(&self) -> usize {
229        return self.gc_refs.lock().unwrap().len();
230    }
231
232    pub fn get_all(&self) -> Vec<GCArc<T>> {
233        self.gc_refs.lock().unwrap().clone()
234    }
235
236    pub fn create(&mut self, obj: T) -> GCArc<T> {
237        let gc_arc = GCArc::new(obj);
238        self.attach(&gc_arc);
239        gc_arc
240    }
241
242    /// 获取当前分配的内存估算值(字节)
243    pub fn allocated_memory(&self) -> usize {
244        self.allocated_memory.load(std::sync::atomic::Ordering::Relaxed)
245    }
246
247    /// 设置内存阈值,None表示禁用内存阈值触发
248    pub fn set_memory_threshold(&mut self, threshold: Option<usize>) {
249        self.memory_threshold = threshold;
250    }
251
252    /// 获取当前内存阈值
253    pub fn memory_threshold(&self) -> Option<usize> {
254        self.memory_threshold
255    }    fn should_collect(&self) -> bool {
256        let current_count = self.gc_refs.lock().unwrap().len();
257        let attach_count = self.attach_count.load(std::sync::atomic::Ordering::Relaxed);
258        let current_memory = self.allocated_memory.load(std::sync::atomic::Ordering::Relaxed);
259
260        if current_count == 0 {
261            return false;
262        }
263
264        // 检查内存阈值
265        if let Some(memory_threshold) = self.memory_threshold {
266            if current_memory >= memory_threshold {
267                return true;
268            }
269        }
270
271        // 检查百分比阈值:当attach次数超过当前对象数的指定百分比时触发回收
272        let threshold = (current_count * self.collection_percentage) / 100;
273        attach_count >= threshold.max(1) // 至少1次attach才触发
274    }
275}
276
277impl<T> Drop for GC<T>
278where
279    T: GCTraceable<T> + 'static,
280{    fn drop(&mut self) {
281        // 在垃圾回收器被销毁时,清理所有跟踪的对象。
282        // 这将触发所有对象的 `Drop` 实现。
283        let mut refs = self.gc_refs.lock().unwrap();
284        for gc_arc in refs.drain(..) {
285            // 减少 `attached_gc_count`,表示该对象不再被垃圾回收器跟踪。
286            gc_arc
287                .inner()
288                .attached_gc_count
289                .fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
290            
291            // 从内存计数中减去对象大小
292            let obj_size = std::mem::size_of::<T>() + std::mem::size_of::<GCArc<T>>();
293            self.allocated_memory
294                .fetch_sub(obj_size, std::sync::atomic::Ordering::Relaxed);
295                
296            // 直接调用 `drop` 方法,确保所有对象都被正确释放。
297            // 这将触发每个对象的 `Drop` 实现。
298            drop(gc_arc);
299        }
300    }
301}
302
303#[cfg(test)]
304mod tests {
305    use std::cell::RefCell;
306
307    use super::*;
308    use crate::{arc::GCArcWeak, traceable::GCTraceable};
309
310    struct TestObject {
311        value: Option<GCArcWeak<TestObjectCell>>,
312    }
313
314    impl GCTraceable<TestObjectCell> for TestObject {
315        fn collect(&self, queue: &mut VecDeque<GCArcWeak<TestObjectCell>>) {
316            if let Some(ref weak_ref) = self.value {
317                queue.push_back(weak_ref.clone());
318            }
319        }
320    }
321
322    impl Drop for TestObject {
323        fn drop(&mut self) {
324            println!("Dropping TestObject: address={:p}", self);
325        }
326    }
327
328    struct TestObjectCell(RefCell<TestObject>);
329    impl GCTraceable<TestObjectCell> for TestObjectCell {
330        fn collect(&self, queue: &mut VecDeque<GCArcWeak<TestObjectCell>>) {
331            if let Ok(obj) = self.0.try_borrow() {
332                if let Some(ref weak_ref) = obj.value {
333                    queue.push_back(weak_ref.clone());
334                }
335            }
336        }
337    }
338    impl Drop for TestObjectCell {
339        fn drop(&mut self) {
340            println!("Dropping TestObjectCell: address={:p}", self);
341        }
342    }
343
344    #[test]
345    fn test_gc() {
346        let mut gc: GC<TestObjectCell> = GC::new_with_percentage(20);
347        {
348            let obj1 = gc.create(TestObjectCell {
349                0: RefCell::new(TestObject { value: None }),
350            });
351            let weak_ref = obj1.as_weak();
352            match obj1.as_ref().0.try_borrow_mut() {
353                Ok(mut obj) => {
354                    obj.value = Some(weak_ref);
355                }
356                Err(_) => {
357                    panic!("Failed to borrow TestObjectCell mutably");
358                }
359            }
360            print!("GC object count before collection: {}\n", gc.object_count());
361        }
362        gc.collect();
363        println!("GC completed, all objects should be dropped now.");
364    }
365
366    #[test]
367    fn test_memory_threshold_gc() {
368        // 使用较小的内存阈值(1KB)来测试内存触发
369        let mut gc: GC<TestObjectCell> = GC::new_with_memory_threshold(1024);
370        
371        println!("Initial allocated memory: {} bytes", gc.allocated_memory());
372        
373        // 创建多个对象直到触发内存阈值
374        let mut objects = Vec::new();
375        for i in 0..50 {
376            let obj = gc.create(TestObjectCell {
377                0: RefCell::new(TestObject { value: None }),
378            });
379            objects.push(obj);
380            
381            println!("After creating object {}: allocated={} bytes, object_count={}", 
382                    i + 1, gc.allocated_memory(), gc.object_count());
383            
384            if gc.allocated_memory() > 1024 {
385                break;
386            }
387        }
388        
389        println!("Before collection: allocated={} bytes, object_count={}", 
390                gc.allocated_memory(), gc.object_count());
391        
392        // 释放引用,让对象变成垃圾
393        objects.clear();
394        
395        // 手动触发回收
396        gc.collect();
397        
398        println!("After collection: allocated={} bytes, object_count={}", 
399                gc.allocated_memory(), gc.object_count());
400    }
401
402    #[test]
403    fn test_combined_thresholds_gc() {
404        // 测试同时使用百分比和内存阈值
405        let mut gc: GC<TestObjectCell> = GC::new_with_thresholds(50, 2048); // 50%或2KB
406        
407        println!("Testing combined thresholds: 50% or 2KB");
408        
409        let obj1 = gc.create(TestObjectCell {
410            0: RefCell::new(TestObject { value: None }),
411        });
412        
413        println!("Memory threshold: {:?}", gc.memory_threshold());
414        println!("Allocated memory: {} bytes", gc.allocated_memory());
415        println!("Object count: {}", gc.object_count());
416        
417        // 保持引用以防止被回收
418        let _keep_ref = obj1;
419    }
420}