use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::sync::{Arc, Mutex, atomic::Ordering, RwLock};
use std::time::{Duration, Instant};
use std::sync::atomic::Ordering;
use std::sync::atomic::AtomicU64;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum GCStrategy {
Conservative,
Aggressive,
Adaptive,
Incremental,
}
#[derive(Debug, Clone)]
pub struct GCTuning {
pub heap_threshold: f64, pub allocation_rate_threshold: f64, pub promotion_threshold: usize, pub survivor_ratio: f64, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GCEvent {
pub event_id: u64,
pub event_type: GCEventType,
pub timestamp: DateTime<Utc>,
pub duration: Duration,
pub heap_before: usize,
pub heap_after: usize,
pub collected_bytes: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum GCEventType {
MinorGC,
MajorGC,
FullGC,
IncrementalGC,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GCStatistics {
pub total_gc_runs: u64,
pub total_gc_time: Duration,
pub total_collected_bytes: usize,
pub average_gc_time: f64,
pub gc_frequency: f64, pub last_gc_time: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HeapMetrics {
pub used_heap_size: usize,
pub total_heap_size: usize,
pub heap_utilization: f64,
pub allocation_rate: f64, pub deallocation_rate: f64, }
pub struct AdaptiveGCController {
current_strategy: Arc<RwLock<GCStrategy>>,
tuning: Arc<RwLock<GCTuning>>,
statistics: Arc<RwLock<GCStatistics>>,
recent_events: Arc<RwLock<Vec<GCEvent>>>,
heap_metrics: Arc<RwLock<HeapMetrics>>,
last_gc: Arc<RwLock<Instant>>,
}
impl Default for GCTuning {
fn default() -> Self {
Self {
heap_threshold: 0.7,
allocation_rate_threshold: 10_000_000.0, promotion_threshold: 256 * 1024, survivor_ratio: 0.5,
}
}
}
impl Default for GCStatistics {
fn default() -> Self {
Self {
total_gc_runs: 0,
total_gc_time: Duration::from_secs(0),
total_collected_bytes: 0,
average_gc_time: 0.0,
gc_frequency: 0.0,
last_gc_time: None,
}
}
}
impl Default for HeapMetrics {
fn default() -> Self {
Self {
used_heap_size: 0,
total_heap_size: 100 * 1024 * 1024, heap_utilization: 0.0,
allocation_rate: 0.0,
deallocation_rate: 0.0,
}
}
}
impl AdaptiveGCController {
pub fn new() -> Self {
Self {
current_strategy: Arc::new(Mutex::new(GCStrategy::Adaptive)))
tuning: Arc::new(Mutex::new(GCTuning::default()))
statistics: Arc::new(Mutex::new(GCStatistics::default()))
recent_events: Arc::new(Mutex::new(Vec::new()))
heap_metrics: Arc::new(Mutex::new(HeapMetrics::default()))
last_gc: Arc::new(Mutex::new(Instant::now()))
}
}
pub async fn trigger_gc(&self, event_type: GCEventType) -> GCEvent {
let event_id: _ = self.generate_event_id();
let start_time: _ = Instant::now();
let mut heap_before = 0;
let mut heap_after = 0;
let collected_bytes;
{
let mut metrics = self.heap_metrics.write().await;
heap_before = metrics.used_heap_size;
collected_bytes = (heap_before as f64 * 0.3) as usize;
heap_after = heap_before - collected_bytes;
metrics.used_heap_size = heap_after;
metrics.heap_utilization = heap_after as f64 / metrics.total_heap_size as f64;
}
let duration: _ = start_time.elapsed();
let event: _ = GCEvent {
event_id,
event_type,
timestamp: Utc::now(),
duration,
heap_before,
heap_after,
collected_bytes,
};
{
let mut events = self.recent_events.write().await;
events.push(event.clone());
if events.len() > 100 {
events.remove(0);
}
}
{
let mut stats = self.statistics.write().await;
stats.total_gc_runs += 1;
stats.total_gc_time += duration;
stats.total_collected_bytes += collected_bytes;
stats.average_gc_time = stats.total_gc_time.as_nanos() as f64 / stats.total_gc_runs as f64 / 1_000_000.0; stats.last_gc_time = Some(Utc::now());
stats.gc_frequency = stats.total_gc_runs as f64 / 60.0; }
{
let mut last_gc = self.last_gc.write().await;
*last_gc = Instant::now();
}
self.adjust_strategy().await;
event
}
pub async fn should_gc(&self) -> bool {
let metrics: _ = self.heap_metrics.read().await;
let tuning: _ = self.tuning.read().await;
let last_gc: _ = self.last_gc.read().await;
if metrics.heap_utilization > tuning.heap_threshold {
return true;
}
if metrics.allocation_rate > tuning.allocation_rate_threshold {
return true;
}
if last_gc.elapsed() > Duration::from_secs(1) {
if metrics.heap_utilization > 0.5 {
return true;
}
}
false
}
pub async fn adjust_strategy(&self) {
let stats: _ = self.statistics.read().await;
let metrics: _ = self.heap_metrics.read().await;
let mut strategy = self.current_strategy.write().await;
if stats.average_gc_time > 100.0 { *strategy = GCStrategy::Conservative;
} else if stats.gc_frequency < 1.0 && metrics.heap_utilization < 0.5 {
*strategy = GCStrategy::Aggressive;
} else {
*strategy = GCStrategy::Adaptive;
}
}
pub async fn get_statistics(&self) -> GCStatistics {
self.statistics.read().await.clone()
}
pub async fn get_heap_metrics(&self) -> HeapMetrics {
self.heap_metrics.read().await.clone()
}
pub async fn get_recent_events(&self, count: usize) -> Vec<GCEvent> {
let events: _ = self.recent_events.read().await;
let count: _ = count.min(events.len());
events.iter().rev().take(count).cloned().collect()
}
pub async fn update_heap_metrics(&self, used_size: usize, total_size: usize, alloc_rate: f64, dealloc_rate: f64) {
let mut metrics = self.heap_metrics.write().await;
metrics.used_heap_size = used_size;
metrics.total_heap_size = total_size;
metrics.heap_utilization = used_size as f64 / total_size as f64;
metrics.allocation_rate = alloc_rate;
metrics.deallocation_rate = dealloc_rate;
}
fn generate_event_id(&self) -> u64 {
static COUNTER: AtomicU64 = AtomicU64::new(0);
COUNTER.fetch_add(1, Ordering::SeqCst)
}
}
#[cfg(test)]
mod tests {
#[tokio::test]
async fn test_adaptive_gc() {
let gc: _ = AdaptiveGCController::new();
assert_eq!(gc.get_statistics().await.total_gc_runs, 0);
let event: _ = gc.trigger_gc(GCEventType::MinorGC).await;
assert_eq!(event.event_type, GCEventType::MinorGC);
assert!(event.collected_bytes > 0);
let stats: _ = gc.get_statistics().await;
assert_eq!(stats.total_gc_runs, 1);
assert!(stats.total_collected_bytes > 0);
gc.update_heap_metrics(80_000_000, 100_000_000, 5_000_000.0, 2_000_000.0).await;
let should_gc: _ = gc.should_gc().await;
assert!(true); }
}