gc-optimizer.ts•35.4 kB
/**
* Garbage Collection Optimizer for GEPA
*
* Provides comprehensive GC optimization strategies, object pooling, and memory management
* for high-performance GEPA workloads with adaptive tuning and real-time monitoring.
*/
import { EventEmitter } from 'events';
import { performance } from 'perf_hooks';
import { PerformanceTracker, PerformanceMetricData } from '../services/performance-tracker';
import { MemoryLeakDetector, MemoryLeakIntegration } from './memory-leak-detector';
/**
* GC optimization strategies based on workload characteristics
*/
export interface GCOptimizationStrategy {
name: string;
description: string;
targetWorkloads: string[];
gcTuning: {
/** Maximum heap size before forcing GC (MB) */
maxHeapSize: number;
/** Trigger GC when heap usage exceeds this percentage */
heapUsageThreshold: number;
/** Time between forced GC cycles (ms) */
forcedGCInterval: number;
/** Use incremental GC for large heaps */
incrementalGC: boolean;
/** Maximum pause time for incremental GC (ms) */
maxPauseTime: number;
};
objectLifecycle: {
/** Short-lived object threshold (ms) */
shortLivedThreshold: number;
/** Long-lived object threshold (ms) */
longLivedThreshold: number;
/** Enable generational GC optimization */
generationalOptimization: boolean;
};
}
/**
* Object pool configuration for different object types
*/
export interface ObjectPoolConfig {
/** Pool name/identifier */
name: string;
/** Maximum pool size */
maxSize: number;
/** Minimum pool size to maintain */
minSize: number;
/** Object factory function */
factory: () => any;
/** Object reset function */
reset?: (obj: any) => void;
/** Object validation function */
validate?: (obj: any) => boolean;
/** Pool eviction strategy */
evictionStrategy: 'lru' | 'fifo' | 'random' | 'ttl';
/** TTL for objects in pool (ms) */
ttl?: number;
/** Enable pool size auto-tuning */
autoTune: boolean;
}
/**
* Memory management strategy configuration
*/
export interface MemoryManagementConfig {
/** Enable lazy cleanup */
lazyCleanup: boolean;
/** Cleanup trigger threshold (memory pressure level 0-1) */
cleanupThreshold: number;
/** Buffer reuse patterns */
bufferReuse: {
enabled: boolean;
poolSizes: number[];
maxBuffers: number;
};
/** Resource recycling configuration */
resourceRecycling: {
enabled: boolean;
recycleInterval: number;
maxRecycleAge: number;
};
}
/**
* GC metrics collected during optimization
*/
export interface GCMetrics {
timestamp: number;
type: 'minor' | 'major' | 'incremental' | 'forced';
duration: number;
heapBefore: number;
heapAfter: number;
heapReclaimed: number;
pauseTime: number;
efficiency: number;
triggerReason: string;
}
/**
* Object pool statistics
*/
export interface PoolStatistics {
name: string;
currentSize: number;
maxSize: number;
hits: number;
misses: number;
creates: number;
destroys: number;
hitRate: number;
utilizationRate: number;
averageLifetime: number;
}
/**
* Memory pressure levels for adaptive responses
*/
export type MemoryPressureLevel = 'low' | 'medium' | 'high' | 'critical';
/**
* Memory pressure response handler
*/
export interface MemoryPressureHandler {
level: MemoryPressureLevel;
priority: number;
action: (metrics: GCMetrics) => Promise<void>;
description: string;
}
/**
* Main Garbage Collection Optimizer
*/
export class GarbageCollectionOptimizer extends EventEmitter {
private performanceTracker: PerformanceTracker;
private memoryLeakDetector: MemoryLeakDetector;
private isEnabled = true;
private monitoringInterval?: ReturnType<typeof setInterval>;
private forcedGCInterval?: ReturnType<typeof setInterval>;
// GC optimization state
private currentStrategy: GCOptimizationStrategy;
private gcMetricsHistory: GCMetrics[] = [];
private objectPools = new Map<string, ObjectPool<any>>();
private memoryPressureHandlers: MemoryPressureHandler[] = [];
private bufferPools = new Map<number, Buffer[]>();
// Adaptive tuning state
private adaptiveTuning = {
enabled: true,
learningRate: 0.1,
performanceBaseline: 0,
lastTuningTime: 0,
tuningCooldown: 60000, // 1 minute
};
// Memory management configuration
private memoryConfig: MemoryManagementConfig = {
lazyCleanup: true,
cleanupThreshold: 0.8,
bufferReuse: {
enabled: true,
poolSizes: [1024, 4096, 16384, 65536, 262144], // Common buffer sizes
maxBuffers: 100,
},
resourceRecycling: {
enabled: true,
recycleInterval: 30000, // 30 seconds
maxRecycleAge: 300000, // 5 minutes
},
};
constructor(
performanceTracker: PerformanceTracker,
memoryLeakDetector?: MemoryLeakDetector
) {
super();
this.performanceTracker = performanceTracker;
this.memoryLeakDetector = memoryLeakDetector || MemoryLeakIntegration.getDetector()!;
// Initialize with balanced strategy
this.currentStrategy = this.createBalancedStrategy();
this.initializeObjectPools();
this.initializeMemoryPressureHandlers();
this.initializeBufferPools();
this.startMonitoring();
this.setupGCHooks();
}
/**
* Set GC optimization strategy based on workload type
*/
setOptimizationStrategy(workloadType: string): void {
let strategy: GCOptimizationStrategy;
switch (workloadType) {
case 'high-throughput':
strategy = this.createHighThroughputStrategy();
break;
case 'low-latency':
strategy = this.createLowLatencyStrategy();
break;
case 'memory-intensive':
strategy = this.createMemoryIntensiveStrategy();
break;
case 'batch-processing':
strategy = this.createBatchProcessingStrategy();
break;
default:
strategy = this.createBalancedStrategy();
}
this.currentStrategy = strategy;
this.applyStrategy(strategy);
this.emit('strategyChanged', { workloadType, strategy });
}
/**
* Create and register an object pool
*/
createObjectPool<T>(config: ObjectPoolConfig): ObjectPool<T> {
const pool = new ObjectPool<T>(config);
this.objectPools.set(config.name, pool);
// Hook pool events for monitoring
pool.on('hit', (stats) => this.emit('poolHit', { pool: config.name, stats }));
pool.on('miss', (stats) => this.emit('poolMiss', { pool: config.name, stats }));
pool.on('eviction', (stats) => this.emit('poolEviction', { pool: config.name, stats }));
this.emit('poolCreated', { name: config.name, config });
return pool;
}
/**
* Get an object pool by name
*/
getObjectPool<T>(name: string): ObjectPool<T> | undefined {
return this.objectPools.get(name) as ObjectPool<T>;
}
/**
* Force garbage collection with optimization
*/
async forceGarbageCollection(reason: string = 'manual'): Promise<GCMetrics> {
const startTime = performance.now();
const beforeMemory = process.memoryUsage();
// Pre-GC cleanup
await this.performLazyCleanup();
// Force GC if available
if (global.gc) {
global.gc();
}
const endTime = performance.now();
const afterMemory = process.memoryUsage();
const metrics: GCMetrics = {
timestamp: Date.now(),
type: 'forced',
duration: endTime - startTime,
heapBefore: beforeMemory.heapUsed,
heapAfter: afterMemory.heapUsed,
heapReclaimed: beforeMemory.heapUsed - afterMemory.heapUsed,
pauseTime: endTime - startTime,
efficiency: (beforeMemory.heapUsed - afterMemory.heapUsed) / beforeMemory.heapUsed,
triggerReason: reason,
};
this.recordGCMetrics(metrics);
return metrics;
}
/**
* Get buffer from reuse pool or create new one
*/
getBuffer(size: number): Buffer {
if (!this.memoryConfig.bufferReuse.enabled) {
return Buffer.alloc(size);
}
// Find closest pool size
const poolSize = this.memoryConfig.bufferReuse.poolSizes
.find(s => s >= size) || size;
const pool = this.bufferPools.get(poolSize);
if (pool && pool.length > 0) {
const buffer = pool.pop()!;
// Reset buffer content
buffer.fill(0);
this.performanceTracker.recordMetric({
id: `buffer-reuse-${Date.now()}`,
name: 'buffer-reuse',
category: 'memory-optimization',
timestamp: Date.now(),
data: { size: poolSize, poolSize: pool.length } as PerformanceMetricData,
});
return buffer.subarray(0, size);
}
return Buffer.alloc(size);
}
/**
* Return buffer to reuse pool
*/
returnBuffer(buffer: Buffer): void {
if (!this.memoryConfig.bufferReuse.enabled) {
return;
}
const size = buffer.length;
const poolSize = this.memoryConfig.bufferReuse.poolSizes
.find(s => s >= size);
if (poolSize) {
const pool = this.bufferPools.get(poolSize);
if (pool && pool.length < this.memoryConfig.bufferReuse.maxBuffers) {
// Ensure buffer is the right size
const poolBuffer = poolSize === size ? buffer : Buffer.alloc(poolSize);
if (poolSize !== size) {
buffer.copy(poolBuffer);
}
pool.push(poolBuffer);
}
}
}
/**
* Analyze memory allocation patterns
*/
analyzeAllocationPatterns(): {
hotspots: Array<{ component: string; allocationsPerSecond: number; averageSize: number }>;
patterns: Array<{ pattern: string; frequency: number; impact: number }>;
recommendations: string[];
} {
const gcHistory = this.gcMetricsHistory.slice(-50); // Last 50 GC cycles
const recommendations: string[] = [];
// Analyze GC frequency and efficiency
const avgGCInterval = this.calculateAverageGCInterval(gcHistory);
const avgEfficiency = gcHistory.reduce((sum, gc) => sum + gc.efficiency, 0) / gcHistory.length;
if (avgGCInterval < 5000) { // Less than 5 seconds
recommendations.push('High GC frequency detected - consider object pooling');
}
if (avgEfficiency < 0.2) { // Less than 20% memory reclaimed
recommendations.push('Low GC efficiency - check for memory leaks');
}
// Analyze object pool performance
const poolStats = Array.from(this.objectPools.values()).map(pool => pool.getStatistics());
const lowHitRatePools = poolStats.filter(stats => stats.hitRate < 0.5);
if (lowHitRatePools.length > 0) {
recommendations.push(`Object pools with low hit rates: ${lowHitRatePools.map(p => p.name).join(', ')}`);
}
return {
hotspots: [], // Would be populated with real allocation tracking
patterns: [], // Would be populated with pattern analysis
recommendations,
};
}
/**
* Get comprehensive memory optimization statistics
*/
getOptimizationStatistics(): {
gcMetrics: {
totalCollections: number;
averageDuration: number;
averageEfficiency: number;
memoryReclaimed: number;
};
objectPools: PoolStatistics[];
memoryPressure: {
currentLevel: MemoryPressureLevel;
responseTime: number;
handlersExecuted: number;
};
bufferReuse: {
totalReuses: number;
poolUtilization: Array<{ size: number; usage: number }>;
memoryEfficiency: number;
};
adaptiveTuning: {
enabled: boolean;
lastTuning: number;
performanceGain: number;
strategyEffectiveness: number;
};
} {
const gcHistory = this.gcMetricsHistory;
const poolStats = Array.from(this.objectPools.values()).map(pool => pool.getStatistics());
return {
gcMetrics: {
totalCollections: gcHistory.length,
averageDuration: gcHistory.reduce((sum, gc) => sum + gc.duration, 0) / gcHistory.length || 0,
averageEfficiency: gcHistory.reduce((sum, gc) => sum + gc.efficiency, 0) / gcHistory.length || 0,
memoryReclaimed: gcHistory.reduce((sum, gc) => sum + gc.heapReclaimed, 0),
},
objectPools: poolStats,
memoryPressure: {
currentLevel: this.getCurrentMemoryPressureLevel(),
responseTime: 0, // Would track actual response times
handlersExecuted: 0, // Would track handler executions
},
bufferReuse: {
totalReuses: 0, // Would track from metrics
poolUtilization: Array.from(this.bufferPools.entries()).map(([size, buffers]) => ({
size,
usage: buffers.length / this.memoryConfig.bufferReuse.maxBuffers,
})),
memoryEfficiency: 0, // Would calculate from reuse metrics
},
adaptiveTuning: {
enabled: this.adaptiveTuning.enabled,
lastTuning: this.adaptiveTuning.lastTuningTime,
performanceGain: 0, // Would calculate from performance metrics
strategyEffectiveness: 0, // Would calculate from strategy performance
},
};
}
/**
* Shutdown GC optimizer
*/
shutdown(): void {
this.isEnabled = false;
if (this.monitoringInterval) {
clearInterval(this.monitoringInterval);
}
if (this.forcedGCInterval) {
clearInterval(this.forcedGCInterval);
}
// Shutdown object pools
for (const pool of Array.from(this.objectPools.values())) {
pool.shutdown();
}
this.emit('shutdown');
}
// Private Methods
private initializeObjectPools(): void {
// Create pools for common GEPA objects
// Candidate object pool
this.createObjectPool({
name: 'candidates',
maxSize: 1000,
minSize: 100,
factory: () => ({
id: '',
fitness: 0,
objectives: [],
metadata: {},
timestamp: 0,
}),
reset: (obj) => {
obj.id = '';
obj.fitness = 0;
obj.objectives.length = 0;
obj.metadata = {};
obj.timestamp = 0;
},
evictionStrategy: 'lru',
autoTune: true,
});
// Trajectory data pool
this.createObjectPool({
name: 'trajectory-data',
maxSize: 500,
minSize: 50,
factory: () => ({
steps: [],
metadata: {},
startTime: 0,
endTime: 0,
}),
reset: (obj) => {
obj.steps.length = 0;
obj.metadata = {};
obj.startTime = 0;
obj.endTime = 0;
},
evictionStrategy: 'ttl',
ttl: 300000, // 5 minutes
autoTune: true,
});
// Analysis result pool
this.createObjectPool({
name: 'analysis-results',
maxSize: 200,
minSize: 20,
factory: () => ({
type: '',
data: {},
score: 0,
timestamp: 0,
cached: false,
}),
reset: (obj) => {
obj.type = '';
obj.data = {};
obj.score = 0;
obj.timestamp = 0;
obj.cached = false;
},
evictionStrategy: 'fifo',
autoTune: true,
});
}
private initializeMemoryPressureHandlers(): void {
// Critical level - immediate action
this.memoryPressureHandlers.push({
level: 'critical',
priority: 1,
action: async (_metrics) => {
await this.forceGarbageCollection('critical-pressure');
await this.performAggressiveCleanup();
},
description: 'Force GC and aggressive cleanup',
});
// High level - proactive cleanup
this.memoryPressureHandlers.push({
level: 'high',
priority: 2,
action: async (_metrics) => {
await this.performLazyCleanup();
await this.evictLRUObjects();
},
description: 'Lazy cleanup and LRU eviction',
});
// Medium level - pool optimization
this.memoryPressureHandlers.push({
level: 'medium',
priority: 3,
action: async (_metrics) => {
await this.optimizeObjectPools();
},
description: 'Object pool optimization',
});
// Low level - background maintenance
this.memoryPressureHandlers.push({
level: 'low',
priority: 4,
action: async (_metrics) => {
await this.performBackgroundMaintenance();
},
description: 'Background maintenance',
});
}
private initializeBufferPools(): void {
for (const size of this.memoryConfig.bufferReuse.poolSizes) {
this.bufferPools.set(size, []);
}
}
private startMonitoring(): void {
this.monitoringInterval = setInterval(async () => {
if (!this.isEnabled) return;
try {
// Check memory pressure and respond
const pressureLevel = this.getCurrentMemoryPressureLevel();
await this.respondToMemoryPressure(pressureLevel);
// Perform adaptive tuning if needed
if (this.adaptiveTuning.enabled) {
await this.performAdaptiveTuning();
}
// Auto-tune object pools
await this.autoTuneObjectPools();
// Record memory metrics
this.performanceTracker.recordMemorySnapshot('gc-optimizer');
} catch (error) {
this.emit('monitoringError', error);
}
}, 5000); // Every 5 seconds
// Setup forced GC interval based on strategy
if (this.currentStrategy.gcTuning.forcedGCInterval > 0) {
this.forcedGCInterval = setInterval(async () => {
if (this.shouldForceGC()) {
await this.forceGarbageCollection('scheduled');
}
}, this.currentStrategy.gcTuning.forcedGCInterval);
}
}
private setupGCHooks(): void {
// Hook into V8 GC events if available
if (process.env.NODE_ENV !== 'production') {
// Development monitoring
const originalGC = global.gc;
if (originalGC) {
global.gc = async () => {
const startTime = performance.now();
const beforeMemory = process.memoryUsage();
originalGC();
const endTime = performance.now();
const afterMemory = process.memoryUsage();
const metrics: GCMetrics = {
timestamp: Date.now(),
type: 'forced',
duration: endTime - startTime,
heapBefore: beforeMemory.heapUsed,
heapAfter: afterMemory.heapUsed,
heapReclaimed: beforeMemory.heapUsed - afterMemory.heapUsed,
pauseTime: endTime - startTime,
efficiency: (beforeMemory.heapUsed - afterMemory.heapUsed) / beforeMemory.heapUsed,
triggerReason: 'manual',
};
this.recordGCMetrics(metrics);
};
}
}
}
private async performLazyCleanup(): Promise<void> {
// Clean up weak references in all object pools
for (const pool of Array.from(this.objectPools.values())) {
await pool.performMaintenanceCleanup();
}
// Clean up buffer pools
for (const [_size, buffers] of Array.from(this.bufferPools)) {
if (buffers.length > this.memoryConfig.bufferReuse.maxBuffers * 0.8) {
// Remove oldest buffers
const toRemove = Math.floor(buffers.length * 0.2);
buffers.splice(0, toRemove);
}
}
}
private async performAggressiveCleanup(): Promise<void> {
// Force cleanup of all object pools
for (const pool of Array.from(this.objectPools.values())) {
await pool.forceEviction(0.5); // Evict 50% of objects
}
// Clear buffer pools
for (const buffers of Array.from(this.bufferPools.values())) {
buffers.length = 0;
}
// Force memory leak detector cleanup
if (this.memoryLeakDetector) {
await this.memoryLeakDetector.forceCleanup();
}
}
private async evictLRUObjects(): Promise<void> {
for (const pool of Array.from(this.objectPools.values())) {
await pool.evictLRU(0.2); // Evict 20% of LRU objects
}
}
private async optimizeObjectPools(): Promise<void> {
for (const pool of Array.from(this.objectPools.values())) {
const stats = pool.getStatistics();
// Resize pool based on utilization
if (stats.utilizationRate > 0.9) {
pool.increaseSize(Math.ceil(stats.maxSize * 0.2));
} else if (stats.utilizationRate < 0.3) {
pool.decreaseSize(Math.ceil(stats.maxSize * 0.1));
}
}
}
private async performBackgroundMaintenance(): Promise<void> {
// Compact object pools
for (const pool of Array.from(this.objectPools.values())) {
await pool.compact();
}
// Analyze allocation patterns
const analysis = this.analyzeAllocationPatterns();
this.emit('allocationAnalysis', analysis);
}
private getCurrentMemoryPressureLevel(): MemoryPressureLevel {
const memoryUsage = process.memoryUsage();
const heapUsageRatio = memoryUsage.heapUsed / memoryUsage.heapTotal;
if (heapUsageRatio > 0.9) return 'critical';
if (heapUsageRatio > 0.8) return 'high';
if (heapUsageRatio > 0.6) return 'medium';
return 'low';
}
private async respondToMemoryPressure(level: MemoryPressureLevel): Promise<void> {
const handlers = this.memoryPressureHandlers
.filter(h => h.level === level)
.sort((a, b) => a.priority - b.priority);
for (const handler of handlers) {
try {
await handler.action({} as GCMetrics);
this.emit('pressureHandlerExecuted', { level, handler: handler.description });
} catch (error) {
this.emit('pressureHandlerError', { level, handler: handler.description, error });
}
}
}
private async performAdaptiveTuning(): Promise<void> {
const now = Date.now();
if (now - this.adaptiveTuning.lastTuningTime < this.adaptiveTuning.tuningCooldown) {
return;
}
// Analyze recent performance
const recentMetrics = this.gcMetricsHistory.slice(-10);
if (recentMetrics.length < 5) return;
const avgEfficiency = recentMetrics.reduce((sum, gc) => sum + gc.efficiency, 0) / recentMetrics.length;
const avgDuration = recentMetrics.reduce((sum, gc) => sum + gc.duration, 0) / recentMetrics.length;
// Adjust strategy based on performance
if (avgEfficiency < 0.3 && avgDuration > 50) {
// Poor efficiency and high duration - reduce forced GC frequency
this.currentStrategy.gcTuning.forcedGCInterval *= 1.2;
this.currentStrategy.gcTuning.heapUsageThreshold += 0.05;
} else if (avgEfficiency > 0.7 && avgDuration < 20) {
// Good efficiency and low duration - can be more aggressive
this.currentStrategy.gcTuning.forcedGCInterval *= 0.9;
this.currentStrategy.gcTuning.heapUsageThreshold -= 0.05;
}
this.adaptiveTuning.lastTuningTime = now;
this.emit('adaptiveTuning', { avgEfficiency, avgDuration, strategy: this.currentStrategy });
}
private async autoTuneObjectPools(): Promise<void> {
for (const pool of Array.from(this.objectPools.values())) {
if (pool.config.autoTune) {
await pool.autoTune();
}
}
}
private shouldForceGC(): boolean {
const memoryUsage = process.memoryUsage();
const heapUsageRatio = memoryUsage.heapUsed / memoryUsage.heapTotal;
return heapUsageRatio > this.currentStrategy.gcTuning.heapUsageThreshold ||
memoryUsage.heapUsed > this.currentStrategy.gcTuning.maxHeapSize * 1024 * 1024;
}
private recordGCMetrics(metrics: GCMetrics): void {
this.gcMetricsHistory.push(metrics);
// Keep only last 1000 GC cycles
if (this.gcMetricsHistory.length > 1000) {
this.gcMetricsHistory = this.gcMetricsHistory.slice(-1000);
}
// Record in performance tracker
this.performanceTracker.recordMetric({
id: `gc-${metrics.timestamp}`,
name: 'garbage-collection',
category: 'memory-optimization',
timestamp: metrics.timestamp,
duration: metrics.duration,
data: metrics as unknown as PerformanceMetricData,
});
this.emit('gcMetrics', metrics);
}
private calculateAverageGCInterval(gcHistory: GCMetrics[]): number {
if (gcHistory.length < 2) return 0;
const intervals = [];
for (let i = 1; i < gcHistory.length; i++) {
const current = gcHistory[i];
const previous = gcHistory[i - 1];
if (current && previous) {
intervals.push(current.timestamp - previous.timestamp);
}
}
return intervals.reduce((sum, interval) => sum + interval, 0) / intervals.length;
}
private applyStrategy(strategy: GCOptimizationStrategy): void {
// Update forced GC interval
if (this.forcedGCInterval) {
clearInterval(this.forcedGCInterval);
}
if (strategy.gcTuning.forcedGCInterval > 0) {
this.forcedGCInterval = setInterval(async () => {
if (this.shouldForceGC()) {
await this.forceGarbageCollection('scheduled');
}
}, strategy.gcTuning.forcedGCInterval);
}
}
// Strategy creation methods
private createBalancedStrategy(): GCOptimizationStrategy {
return {
name: 'balanced',
description: 'Balanced optimization for general workloads',
targetWorkloads: ['general', 'mixed'],
gcTuning: {
maxHeapSize: 512,
heapUsageThreshold: 0.8,
forcedGCInterval: 30000,
incrementalGC: true,
maxPauseTime: 50,
},
objectLifecycle: {
shortLivedThreshold: 1000,
longLivedThreshold: 60000,
generationalOptimization: true,
},
};
}
private createHighThroughputStrategy(): GCOptimizationStrategy {
return {
name: 'high-throughput',
description: 'Optimized for maximum throughput',
targetWorkloads: ['batch-processing', 'data-intensive'],
gcTuning: {
maxHeapSize: 1024,
heapUsageThreshold: 0.9,
forcedGCInterval: 60000,
incrementalGC: false,
maxPauseTime: 200,
},
objectLifecycle: {
shortLivedThreshold: 5000,
longLivedThreshold: 300000,
generationalOptimization: true,
},
};
}
private createLowLatencyStrategy(): GCOptimizationStrategy {
return {
name: 'low-latency',
description: 'Optimized for minimal pause times',
targetWorkloads: ['real-time', 'interactive'],
gcTuning: {
maxHeapSize: 256,
heapUsageThreshold: 0.7,
forcedGCInterval: 10000,
incrementalGC: true,
maxPauseTime: 10,
},
objectLifecycle: {
shortLivedThreshold: 500,
longLivedThreshold: 10000,
generationalOptimization: true,
},
};
}
private createMemoryIntensiveStrategy(): GCOptimizationStrategy {
return {
name: 'memory-intensive',
description: 'Optimized for large memory workloads',
targetWorkloads: ['large-dataset', 'caching'],
gcTuning: {
maxHeapSize: 2048,
heapUsageThreshold: 0.85,
forcedGCInterval: 45000,
incrementalGC: true,
maxPauseTime: 100,
},
objectLifecycle: {
shortLivedThreshold: 2000,
longLivedThreshold: 600000,
generationalOptimization: false,
},
};
}
private createBatchProcessingStrategy(): GCOptimizationStrategy {
return {
name: 'batch-processing',
description: 'Optimized for batch processing workloads',
targetWorkloads: ['batch', 'background-processing'],
gcTuning: {
maxHeapSize: 1536,
heapUsageThreshold: 0.95,
forcedGCInterval: 120000,
incrementalGC: false,
maxPauseTime: 500,
},
objectLifecycle: {
shortLivedThreshold: 10000,
longLivedThreshold: 1800000,
generationalOptimization: false,
},
};
}
}
/**
* Object Pool Implementation with Advanced Features
*/
export class ObjectPool<T> extends EventEmitter {
public readonly config: ObjectPoolConfig;
private pool: Array<{ object: T; timestamp: number; accessCount: number }> = [];
private statistics = {
hits: 0,
misses: 0,
creates: 0,
destroys: 0,
evictions: 0,
totalAccesses: 0,
};
constructor(config: ObjectPoolConfig) {
super();
this.config = config;
// Store performance tracker reference (may be unused in current implementation)
// This allows for future performance tracking integration
this.initializePool();
}
/**
* Get an object from the pool
*/
get(): T {
const now = Date.now();
this.statistics.totalAccesses++;
// Try to find available object
for (let i = 0; i < this.pool.length; i++) {
const poolItem = this.pool[i];
// Ensure poolItem exists and check TTL if configured
if (!poolItem) {
continue;
}
if (this.config.ttl && now - poolItem.timestamp > this.config.ttl) {
this.pool.splice(i, 1);
this.statistics.evictions++;
continue;
}
// Validate object if validator provided
if (this.config.validate && !this.config.validate(poolItem.object)) {
this.pool.splice(i, 1);
this.statistics.evictions++;
continue;
}
// Found valid object
this.pool.splice(i, 1);
poolItem.accessCount++;
this.statistics.hits++;
this.emit('hit', this.getStatistics());
return poolItem.object;
}
// No available object - create new one
this.statistics.misses++;
this.statistics.creates++;
const newObject = this.config.factory();
this.emit('miss', this.getStatistics());
return newObject;
}
/**
* Return an object to the pool
*/
return(object: T): void {
if (this.pool.length >= this.config.maxSize) {
// Pool is full - evict based on strategy
this.evictObject();
}
// Reset object if reset function provided
if (this.config.reset) {
this.config.reset(object);
}
this.pool.push({
object,
timestamp: Date.now(),
accessCount: 0,
});
}
/**
* Get pool statistics
*/
getStatistics(): PoolStatistics {
const hitRate = this.statistics.totalAccesses > 0 ?
this.statistics.hits / this.statistics.totalAccesses : 0;
const utilizationRate = this.pool.length / this.config.maxSize;
const averageLifetime = this.pool.length > 0 ?
this.pool.reduce((sum, item) => sum + (Date.now() - item.timestamp), 0) / this.pool.length : 0;
return {
name: this.config.name,
currentSize: this.pool.length,
maxSize: this.config.maxSize,
hits: this.statistics.hits,
misses: this.statistics.misses,
creates: this.statistics.creates,
destroys: this.statistics.destroys,
hitRate,
utilizationRate,
averageLifetime,
};
}
/**
* Force eviction of objects
*/
async forceEviction(percentage: number): Promise<number> {
const toEvict = Math.floor(this.pool.length * percentage);
const evicted = this.pool.splice(0, toEvict);
this.statistics.evictions += evicted.length;
this.statistics.destroys += evicted.length;
this.emit('eviction', { count: evicted.length, stats: this.getStatistics() });
return evicted.length;
}
/**
* Evict LRU objects
*/
async evictLRU(percentage: number): Promise<number> {
// Sort by access count and timestamp
this.pool.sort((a, b) => {
if (a.accessCount !== b.accessCount) {
return a.accessCount - b.accessCount;
}
return a.timestamp - b.timestamp;
});
const toEvict = Math.floor(this.pool.length * percentage);
const evicted = this.pool.splice(0, toEvict);
this.statistics.evictions += evicted.length;
this.statistics.destroys += evicted.length;
return evicted.length;
}
/**
* Increase pool size
*/
increaseSize(amount: number): void {
this.config.maxSize += amount;
this.emit('resized', { newSize: this.config.maxSize, change: amount });
}
/**
* Decrease pool size
*/
decreaseSize(amount: number): void {
this.config.maxSize = Math.max(this.config.minSize, this.config.maxSize - amount);
// Evict excess objects if needed
if (this.pool.length > this.config.maxSize) {
const excess = this.pool.length - this.config.maxSize;
this.forceEviction(excess / this.pool.length);
}
this.emit('resized', { newSize: this.config.maxSize, change: -amount });
}
/**
* Compact pool by removing invalid objects
*/
async compact(): Promise<number> {
const before = this.pool.length;
const now = Date.now();
this.pool = this.pool.filter(item => {
// Check TTL
if (this.config.ttl && now - item.timestamp > this.config.ttl) {
return false;
}
// Check validation
if (this.config.validate && !this.config.validate(item.object)) {
return false;
}
return true;
});
const removed = before - this.pool.length;
this.statistics.evictions += removed;
this.statistics.destroys += removed;
return removed;
}
/**
* Auto-tune pool size based on usage patterns
*/
async autoTune(): Promise<void> {
const stats = this.getStatistics();
// Increase size if hit rate is low and utilization is high
if (stats.hitRate < 0.7 && stats.utilizationRate > 0.9) {
this.increaseSize(Math.ceil(this.config.maxSize * 0.1));
}
// Decrease size if utilization is consistently low
if (stats.utilizationRate < 0.3 && this.pool.length > this.config.minSize) {
this.decreaseSize(Math.ceil(this.config.maxSize * 0.05));
}
}
/**
* Perform maintenance cleanup
*/
async performMaintenanceCleanup(): Promise<void> {
await this.compact();
}
/**
* Shutdown pool
*/
shutdown(): void {
this.pool.length = 0;
this.removeAllListeners();
}
// Private Methods
private initializePool(): void {
// Pre-populate pool with minimum objects
for (let i = 0; i < this.config.minSize; i++) {
this.pool.push({
object: this.config.factory(),
timestamp: Date.now(),
accessCount: 0,
});
this.statistics.creates++;
}
}
private evictObject(): void {
if (this.pool.length === 0) return;
let indexToEvict = 0;
switch (this.config.evictionStrategy) {
case 'lru':
// Find least recently used
let oldestTime = this.pool[0]?.timestamp || 0;
for (let i = 1; i < this.pool.length; i++) {
const item = this.pool[i];
if (item && item.timestamp < oldestTime) {
oldestTime = item.timestamp;
indexToEvict = i;
}
}
break;
case 'fifo':
// Remove first (oldest)
indexToEvict = 0;
break;
case 'random':
// Remove random
indexToEvict = Math.floor(Math.random() * this.pool.length);
break;
case 'ttl':
// Find expired or oldest
const now = Date.now();
for (let i = 0; i < this.pool.length; i++) {
const item = this.pool[i];
if (item && this.config.ttl && now - item.timestamp > this.config.ttl) {
indexToEvict = i;
break;
}
}
break;
}
this.pool.splice(indexToEvict, 1);
this.statistics.evictions++;
this.statistics.destroys++;
}
}