1use crate::{GcConfig, GcError, Result};
7use std::collections::VecDeque;
8use std::sync::atomic::{AtomicUsize, Ordering};
9use std::time::{Duration, Instant};
10
11#[derive(Debug)]
13pub struct Generation {
14 pub number: usize,
16
17 current_size: AtomicUsize,
19
20 max_size: usize,
22
23 collection_count: AtomicUsize,
25
26 survivor_count: AtomicUsize,
28
29 last_collection: parking_lot::Mutex<Option<Instant>>,
31
32 promotion_threshold: usize,
34
35 collection_frequency: parking_lot::Mutex<VecDeque<Instant>>,
37}
38
39impl Generation {
40 pub fn new(number: usize, max_size: usize, promotion_threshold: usize) -> Self {
41 Self {
42 number,
43 current_size: AtomicUsize::new(0),
44 max_size,
45 collection_count: AtomicUsize::new(0),
46 survivor_count: AtomicUsize::new(0),
47 last_collection: parking_lot::Mutex::new(None),
48 promotion_threshold,
49 collection_frequency: parking_lot::Mutex::new(VecDeque::new()),
50 }
51 }
52
53 pub fn current_size(&self) -> usize {
55 self.current_size.load(Ordering::Relaxed)
56 }
57
58 pub fn max_size(&self) -> usize {
60 self.max_size
61 }
62
63 pub fn utilization(&self) -> f64 {
65 self.current_size() as f64 / self.max_size as f64
66 }
67
68 pub fn is_full(&self, threshold: f64) -> bool {
70 self.utilization() >= threshold
71 }
72
73 pub fn allocate(&self, size: usize) -> Result<()> {
75 let current = self.current_size.load(Ordering::Relaxed);
76 if current + size > self.max_size {
77 return Err(GcError::OutOfMemory(format!(
78 "Generation {} cannot allocate {} bytes",
79 self.number, size
80 )));
81 }
82
83 self.current_size.fetch_add(size, Ordering::Relaxed);
84 Ok(())
85 }
86
87 pub fn deallocate(&self, size: usize) {
89 self.current_size
90 .fetch_sub(size.min(self.current_size()), Ordering::Relaxed);
91 }
92
93 pub fn record_collection(&self, objects_collected: usize, survivors: usize) {
95 self.collection_count.fetch_add(1, Ordering::Relaxed);
96 self.survivor_count.store(survivors, Ordering::Relaxed);
97
98 let now = Instant::now();
99 *self.last_collection.lock() = Some(now);
100
101 let mut frequency = self.collection_frequency.lock();
103 frequency.push_back(now);
104
105 let cutoff = now - Duration::from_secs(60);
107 while frequency.front().is_some_and(|&t| t < cutoff) {
108 frequency.pop_front();
109 }
110
111 log::debug!(
112 "Generation {} collection: {} collected, {} survivors",
113 self.number,
114 objects_collected,
115 survivors
116 );
117 }
118
119 pub fn collection_count(&self) -> usize {
121 self.collection_count.load(Ordering::Relaxed)
122 }
123
124 pub fn survivor_count(&self) -> usize {
126 self.survivor_count.load(Ordering::Relaxed)
127 }
128
129 pub fn should_promote(&self, object_age: usize) -> bool {
131 object_age >= self.promotion_threshold
132 }
133
134 pub fn time_since_last_collection(&self) -> Option<Duration> {
136 self.last_collection.lock().map(|time| time.elapsed())
137 }
138
139 pub fn collection_frequency(&self) -> f64 {
141 let frequency = self.collection_frequency.lock();
142 if frequency.len() < 2 {
143 return 0.0;
144 }
145
146 let duration = frequency
147 .back()
148 .unwrap()
149 .duration_since(*frequency.front().unwrap());
150 if duration.as_secs_f64() == 0.0 {
151 return 0.0;
152 }
153
154 frequency.len() as f64 / (duration.as_secs_f64() / 60.0)
155 }
156
157 pub fn resize(&mut self, new_max_size: usize) -> Result<()> {
159 if new_max_size < self.current_size() {
160 return Err(GcError::ConfigError(format!(
161 "Cannot shrink generation {} below current size",
162 self.number
163 )));
164 }
165
166 log::info!(
167 "Resizing generation {} from {} to {} bytes",
168 self.number,
169 self.max_size,
170 new_max_size
171 );
172 self.max_size = new_max_size;
173 Ok(())
174 }
175
176 pub fn reset(&self) {
178 self.current_size.store(0, Ordering::Relaxed);
179 self.survivor_count.store(0, Ordering::Relaxed);
180 }
182
183 pub fn stats(&self) -> GenerationStats {
185 GenerationStats {
186 number: self.number,
187 current_size: self.current_size(),
188 max_size: self.max_size,
189 utilization: self.utilization(),
190 collection_count: self.collection_count(),
191 survivor_count: self.survivor_count(),
192 promotion_threshold: self.promotion_threshold,
193 time_since_last_collection: self.time_since_last_collection(),
194 collection_frequency: self.collection_frequency(),
195 }
196 }
197}
198
199#[derive(Debug, Clone)]
201pub struct GenerationStats {
202 pub number: usize,
203 pub current_size: usize,
204 pub max_size: usize,
205 pub utilization: f64,
206 pub collection_count: usize,
207 pub survivor_count: usize,
208 pub promotion_threshold: usize,
209 pub time_since_last_collection: Option<Duration>,
210 pub collection_frequency: f64,
211}
212
213pub struct GenerationalHeap {
215 generations: Vec<Generation>,
217
218 config: GcConfig,
220
221 total_size_limit: usize,
223
224 adaptive_sizing: bool,
226
227 total_promotions: AtomicUsize,
229 total_demotions: AtomicUsize,
230}
231
232impl GenerationalHeap {
233 pub fn new(config: &GcConfig) -> Self {
234 let mut generations = Vec::new();
235
236 let mut gen_size = config.young_generation_size;
238 for i in 0..config.num_generations {
239 generations.push(Generation::new(i, gen_size, config.promotion_threshold));
240 gen_size *= 2; }
242
243 let total_size_limit = if config.max_heap_size > 0 {
244 config.max_heap_size
245 } else {
246 let mut total = config.young_generation_size;
248 let mut size = config.young_generation_size;
249 for _ in 1..config.num_generations {
250 size *= 2;
251 total += size;
252 }
253 total
254 };
255
256 Self {
257 generations,
258 config: config.clone(),
259 total_size_limit,
260 adaptive_sizing: true,
261 total_promotions: AtomicUsize::new(0),
262 total_demotions: AtomicUsize::new(0),
263 }
264 }
265
266 pub fn generation(&self, number: usize) -> Option<&Generation> {
268 self.generations.get(number)
269 }
270
271 pub fn young_generation(&self) -> &Generation {
273 &self.generations[0]
274 }
275
276 pub fn generations(&self) -> &[Generation] {
278 &self.generations
279 }
280
281 pub fn num_generations(&self) -> usize {
283 self.generations.len()
284 }
285
286 pub fn should_collect_minor(&self) -> bool {
288 self.young_generation()
289 .is_full(self.config.minor_gc_threshold)
290 }
291
292 pub fn should_collect_major(&self) -> bool {
294 self.total_utilization() >= self.config.major_gc_threshold
295 }
296
297 pub fn total_utilization(&self) -> f64 {
299 let total_used: usize = self.generations.iter().map(|g| g.current_size()).sum();
300
301 total_used as f64 / self.total_size_limit as f64
302 }
303
304 pub fn total_current_size(&self) -> usize {
306 self.generations.iter().map(|g| g.current_size()).sum()
307 }
308
309 pub fn total_max_size(&self) -> usize {
311 self.generations.iter().map(|g| g.max_size()).sum()
312 }
313
314 pub fn record_promotion(&self, _from_gen: usize, _to_gen: usize, _count: usize) {
316 self.total_promotions.fetch_add(_count, Ordering::Relaxed);
317
318 log::trace!("Promoted {_count} objects from generation {_from_gen} to {_to_gen}");
319 }
320
321 pub fn record_demotion(&self, _from_gen: usize, _to_gen: usize, _count: usize) {
323 self.total_demotions.fetch_add(_count, Ordering::Relaxed);
324
325 log::trace!("Demoted {_count} objects from generation {_from_gen} to {_to_gen}");
326 }
327
328 pub fn adapt_generation_sizes(&mut self) -> Result<()> {
330 if !self.adaptive_sizing {
331 return Ok(());
332 }
333
334 log::debug!("Adapting generation sizes based on allocation patterns");
335
336 let young_gen_freq = self.young_generation().collection_frequency();
341 let young_gen_util = self.young_generation().utilization();
342
343 if young_gen_freq > 10.0 && young_gen_util > 0.8 {
345 let new_size = (self.generations[0].max_size() as f64 * 1.2) as usize;
346 if new_size <= self.total_size_limit / 4 {
347 log::info!("Increasing young generation size to {new_size} bytes");
349 self.generations[0].max_size = new_size;
350 }
351 }
352
353 for i in 1..self.generations.len() {
355 let gen = &self.generations[i];
356 if gen.utilization() < 0.2 && gen.max_size() > self.config.young_generation_size {
357 let new_size = (gen.max_size() as f64 * 0.9) as usize;
358 log::info!("Decreasing generation {i} size to {new_size} bytes");
359 self.generations[i].max_size = new_size.max(self.config.young_generation_size);
360 }
361 }
362
363 Ok(())
364 }
365
366 pub fn reconfigure(&mut self, config: &GcConfig) -> Result<()> {
368 if config.num_generations != self.generations.len() {
370 return Err(GcError::ConfigError(
371 "Cannot change number of generations at runtime".to_string(),
372 ));
373 }
374
375 for gen in self.generations.iter_mut() {
377 gen.promotion_threshold = config.promotion_threshold;
378 }
379
380 self.config = config.clone();
381
382 if config.young_generation_size != self.generations[0].max_size() {
384 self.generations[0].resize(config.young_generation_size)?;
385 }
386
387 Ok(())
388 }
389
390 pub fn stats(&self) -> GenerationalHeapStats {
392 GenerationalHeapStats {
393 generations: self.generations.iter().map(|g| g.stats()).collect(),
394 total_current_size: self.total_current_size(),
395 total_max_size: self.total_max_size(),
396 total_utilization: self.total_utilization(),
397 total_promotions: self.total_promotions.load(Ordering::Relaxed),
398 total_demotions: self.total_demotions.load(Ordering::Relaxed),
399 should_collect_minor: self.should_collect_minor(),
400 should_collect_major: self.should_collect_major(),
401 }
402 }
403}
404
405#[derive(Debug, Clone)]
407pub struct GenerationalHeapStats {
408 pub generations: Vec<GenerationStats>,
409 pub total_current_size: usize,
410 pub total_max_size: usize,
411 pub total_utilization: f64,
412 pub total_promotions: usize,
413 pub total_demotions: usize,
414 pub should_collect_minor: bool,
415 pub should_collect_major: bool,
416}
417
418impl GenerationalHeapStats {
419 pub fn summary(&self) -> String {
421 let mut report = format!(
422 "Generational Heap Summary:\n\
423 Total Size: {} / {} bytes ({:.1}% utilized)\n\
424 Promotions: {}, Demotions: {}\n\
425 Collection Triggers: Minor={}, Major={}\n\n",
426 self.total_current_size,
427 self.total_max_size,
428 self.total_utilization * 100.0,
429 self.total_promotions,
430 self.total_demotions,
431 self.should_collect_minor,
432 self.should_collect_major
433 );
434
435 for gen in &self.generations {
436 report.push_str(&format!(
437 "Generation {}: {} / {} bytes ({:.1}% util), {} collections, freq={:.1}/min\n",
438 gen.number,
439 gen.current_size,
440 gen.max_size,
441 gen.utilization * 100.0,
442 gen.collection_count,
443 gen.collection_frequency
444 ));
445 }
446
447 report
448 }
449}
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454 use crate::GcConfig;
455
456 #[test]
457 fn test_generation_basic() {
458 let gen = Generation::new(0, 1024, 2);
459
460 assert_eq!(gen.number, 0);
461 assert_eq!(gen.max_size(), 1024);
462 assert_eq!(gen.current_size(), 0);
463 assert_eq!(gen.utilization(), 0.0);
464 assert!(!gen.is_full(0.8));
465
466 gen.allocate(512).expect("should allocate");
468 assert_eq!(gen.current_size(), 512);
469 assert_eq!(gen.utilization(), 0.5);
470
471 gen.deallocate(256);
473 assert_eq!(gen.current_size(), 256);
474 }
475
476 #[test]
477 fn test_generation_promotion() {
478 let gen = Generation::new(0, 1024, 2);
479
480 assert!(!gen.should_promote(1));
481 assert!(gen.should_promote(2));
482 assert!(gen.should_promote(3));
483 }
484
485 #[test]
486 fn test_generation_collection_tracking() {
487 let gen = Generation::new(0, 1024, 2);
488
489 assert_eq!(gen.collection_count(), 0);
490 assert_eq!(gen.survivor_count(), 0);
491
492 gen.record_collection(10, 5);
493 assert_eq!(gen.collection_count(), 1);
494 assert_eq!(gen.survivor_count(), 5);
495
496 gen.record_collection(8, 3);
497 assert_eq!(gen.collection_count(), 2);
498 assert_eq!(gen.survivor_count(), 3);
499 }
500
501 #[test]
502 fn test_generational_heap() {
503 let config = GcConfig::default();
504 let heap = GenerationalHeap::new(&config);
505
506 assert_eq!(heap.num_generations(), config.num_generations);
507 assert_eq!(heap.young_generation().number, 0);
508
509 assert!(!heap.should_collect_minor()); assert!(!heap.should_collect_major());
512
513 heap.young_generation()
515 .allocate((config.young_generation_size as f64 * 0.9) as usize)
516 .expect("should allocate");
517
518 assert!(heap.should_collect_minor()); }
520
521 #[test]
522 fn test_heap_statistics() {
523 let config = GcConfig::default();
524 let heap = GenerationalHeap::new(&config);
525
526 let stats = heap.stats();
527 assert_eq!(stats.generations.len(), config.num_generations);
528 assert_eq!(stats.total_current_size, 0);
529 assert!(stats.total_max_size > 0);
530 assert_eq!(stats.total_utilization, 0.0);
531
532 heap.young_generation()
534 .allocate(1000)
535 .expect("should allocate");
536
537 let stats = heap.stats();
538 assert_eq!(stats.total_current_size, 1000);
539 assert!(stats.total_utilization > 0.0);
540 }
541
542 #[test]
543 fn test_generation_resize() {
544 let mut gen = Generation::new(0, 1024, 2);
545
546 gen.resize(2048).expect("should resize");
548 assert_eq!(gen.max_size(), 2048);
549
550 gen.allocate(1500).expect("should allocate");
552
553 assert!(gen.resize(1000).is_err());
555
556 assert!(gen.resize(1500).is_ok());
558 }
559
560 #[test]
561 fn test_heap_total_utilization() {
562 let config = GcConfig::default();
563 let heap = GenerationalHeap::new(&config);
564
565 heap.generation(0)
567 .unwrap()
568 .allocate(1000)
569 .expect("should allocate");
570 heap.generation(1)
571 .unwrap()
572 .allocate(2000)
573 .expect("should allocate");
574
575 let utilization = heap.total_utilization();
576 assert!(utilization > 0.0);
577 assert!(utilization < 1.0);
578
579 let total_used = heap.total_current_size();
580 assert_eq!(total_used, 3000);
581 }
582}