UNPKG

@claude-vector/core

Version:

Core vector search engine for code intelligence

598 lines (510 loc) 17.7 kB
/** * MemoryMonitor - リアルタイムメモリ監視システム * * 機能: * - 連続稼働時のメモリトレンド監視 * - 異常なメモリ増加の検知・アラート * - メモリ使用パターンの統計分析 * - ガベージコレクション最適化 */ import fs from 'fs/promises'; import path from 'path'; import { EventEmitter } from 'events'; export class MemoryMonitor extends EventEmitter { constructor(options = {}) { super(); this.options = { enabled: options.enabled ?? true, monitoringInterval: options.monitoringInterval ?? 30000, // 30秒間隔 alertThresholdMB: options.alertThresholdMB ?? 500, // 500MB threshold memoryLeakThresholdMB: options.memoryLeakThresholdMB ?? 100, // 100MB増加でリーク疑い maxHistorySize: options.maxHistorySize ?? 1000, // 最大履歴サイズ reportOutputFile: options.reportOutputFile ?? './.claude-memory-report.json', enableGCOptimization: options.enableGCOptimization ?? false, verboseLogging: options.verboseLogging ?? false, ...options }; // 監視データ this.memoryHistory = []; this.isMonitoring = false; this.monitoringTimer = null; this.startTime = null; this.baseline = null; // 統計データ this.stats = { peakMemoryUsage: 0, averageMemoryUsage: 0, memoryLeaksDetected: 0, gcOptimizationsPerformed: 0, alertsTriggered: 0, totalMonitoringTime: 0 }; // アラート状態 this.alertState = { highMemoryAlert: false, memoryLeakAlert: false, rapidIncreaseAlert: false, lastAlertTime: null }; } /** * 監視開始 */ startMonitoring(metadata = {}) { if (this.isMonitoring) { this.emit('warning', 'Memory monitoring is already running'); return; } this.isMonitoring = true; this.startTime = Date.now(); this.baseline = this.getCurrentMemoryUsage(); // 初期データ記録 this.recordMemorySnapshot('monitoring_start', metadata); // 定期監視タイマー開始 this.monitoringTimer = setInterval(() => { this.performMonitoringCycle(); }, this.options.monitoringInterval); this.emit('monitoring_started', { startTime: this.startTime, baseline: this.baseline, options: this.options }); if (this.options.verboseLogging) { console.log('🔍 Memory monitoring started'); console.log(` Interval: ${this.options.monitoringInterval}ms`); console.log(` Alert threshold: ${this.options.alertThresholdMB}MB`); console.log(` Baseline memory: ${Math.round(this.baseline.heapUsed / 1024 / 1024)}MB`); } } /** * 監視停止 */ stopMonitoring() { if (!this.isMonitoring) { return; } this.isMonitoring = false; if (this.monitoringTimer) { clearInterval(this.monitoringTimer); this.monitoringTimer = null; } // 最終スナップショット this.recordMemorySnapshot('monitoring_stop'); // 統計更新 this.stats.totalMonitoringTime = Date.now() - this.startTime; this.emit('monitoring_stopped', { duration: this.stats.totalMonitoringTime, finalStats: this.stats, memoryHistorySize: this.memoryHistory.length }); if (this.options.verboseLogging) { console.log('🛑 Memory monitoring stopped'); console.log(` Duration: ${Math.round(this.stats.totalMonitoringTime / 1000)}s`); console.log(` Data points: ${this.memoryHistory.length}`); } } /** * 監視サイクル実行 */ performMonitoringCycle() { const currentMemory = this.getCurrentMemoryUsage(); const snapshot = this.recordMemorySnapshot('periodic_check', { cycleTime: Date.now() }); // アラート検査 this.checkMemoryAlerts(snapshot, currentMemory); // 統計更新 this.updateStats(currentMemory); // GC最適化(必要時) if (this.options.enableGCOptimization && this.shouldPerformGC(snapshot)) { this.performGCOptimization(); } // 履歴サイズ管理 this.manageHistorySize(); // リアルタイムイベント発行 this.emit('memory_update', { currentMemory, snapshot, stats: this.stats, alerts: this.alertState }); } /** * メモリスナップショット記録 */ recordMemorySnapshot(operation, metadata = {}) { const memoryUsage = this.getCurrentMemoryUsage(); const timestamp = Date.now(); const snapshot = { timestamp, operation, memoryUsage, metadata, deltaFromBaseline: this.baseline ? { heapUsed: memoryUsage.heapUsed - this.baseline.heapUsed, heapTotal: memoryUsage.heapTotal - this.baseline.heapTotal, external: memoryUsage.external - this.baseline.external, rss: memoryUsage.rss - this.baseline.rss } : null, timeFromStart: this.startTime ? timestamp - this.startTime : 0 }; this.memoryHistory.push(snapshot); return snapshot; } /** * メモリアラート検査 */ checkMemoryAlerts(snapshot, currentMemory) { const heapUsedMB = currentMemory.heapUsed / 1024 / 1024; const deltaFromBaselineMB = snapshot.deltaFromBaseline ? snapshot.deltaFromBaseline.heapUsed / 1024 / 1024 : 0; // 高メモリ使用量アラート if (heapUsedMB > this.options.alertThresholdMB && !this.alertState.highMemoryAlert) { this.triggerAlert('high_memory_usage', { currentUsageMB: heapUsedMB, thresholdMB: this.options.alertThresholdMB, snapshot }); this.alertState.highMemoryAlert = true; } else if (heapUsedMB <= this.options.alertThresholdMB * 0.8 && this.alertState.highMemoryAlert) { // アラート解除 this.resolveAlert('high_memory_usage', { currentUsageMB: heapUsedMB }); this.alertState.highMemoryAlert = false; } // メモリリークアラート if (deltaFromBaselineMB > this.options.memoryLeakThresholdMB && !this.alertState.memoryLeakAlert) { // 過去5分間のトレンド確認 const recentHistory = this.getRecentHistory(5 * 60 * 1000); // 5分 if (this.detectMemoryLeakPattern(recentHistory)) { this.triggerAlert('memory_leak_suspected', { incrementMB: deltaFromBaselineMB, thresholdMB: this.options.memoryLeakThresholdMB, trendAnalysis: this.analyzeMemoryTrend(recentHistory) }); this.alertState.memoryLeakAlert = true; } } // 急激なメモリ増加アラート const recentSnapshots = this.getRecentHistory(2 * 60 * 1000); // 2分 if (recentSnapshots.length >= 3) { const rapidIncrease = this.detectRapidMemoryIncrease(recentSnapshots); if (rapidIncrease && !this.alertState.rapidIncreaseAlert) { this.triggerAlert('rapid_memory_increase', { increaseMB: rapidIncrease.increaseMB, timeWindowMs: rapidIncrease.timeWindowMs, rate: rapidIncrease.rate }); this.alertState.rapidIncreaseAlert = true; } else if (!rapidIncrease && this.alertState.rapidIncreaseAlert) { this.resolveAlert('rapid_memory_increase'); this.alertState.rapidIncreaseAlert = false; } } } /** * メモリリークパターン検出 */ detectMemoryLeakPattern(history) { if (history.length < 5) return false; // 連続的な増加パターンをチェック let increasingCount = 0; for (let i = 1; i < history.length; i++) { if (history[i].memoryUsage.heapUsed > history[i-1].memoryUsage.heapUsed) { increasingCount++; } } // 80%以上が増加パターンならリーク疑い return (increasingCount / (history.length - 1)) >= 0.8; } /** * 急激なメモリ増加検出 */ detectRapidMemoryIncrease(recentHistory) { if (recentHistory.length < 3) return null; const first = recentHistory[0]; const last = recentHistory[recentHistory.length - 1]; const timeWindowMs = last.timestamp - first.timestamp; const increaseMB = (last.memoryUsage.heapUsed - first.memoryUsage.heapUsed) / 1024 / 1024; const ratePerMinute = (increaseMB / timeWindowMs) * 60 * 1000; // 1分間に50MB以上の増加を急激とみなす if (ratePerMinute > 50 && increaseMB > 20) { return { increaseMB, timeWindowMs, rate: ratePerMinute }; } return null; } /** * アラート発生 */ triggerAlert(alertType, details) { this.stats.alertsTriggered++; this.alertState.lastAlertTime = Date.now(); const alertData = { type: alertType, timestamp: Date.now(), details, recommendations: this.getAlertRecommendations(alertType, details) }; this.emit('memory_alert', alertData); if (this.options.verboseLogging) { console.log(`⚠️ Memory Alert: ${alertType}`); console.log(` Details:`, JSON.stringify(details, null, 2)); } } /** * アラート解除 */ resolveAlert(alertType, details = {}) { this.emit('alert_resolved', { type: alertType, timestamp: Date.now(), details }); if (this.options.verboseLogging) { console.log(`✅ Alert Resolved: ${alertType}`); } } /** * アラート推奨事項取得 */ getAlertRecommendations(alertType, details) { const recommendations = []; switch (alertType) { case 'high_memory_usage': recommendations.push('Consider implementing memory pooling'); recommendations.push('Review large object allocations'); recommendations.push('Force garbage collection if appropriate'); break; case 'memory_leak_suspected': recommendations.push('Review recent code changes for memory leaks'); recommendations.push('Check for unclosed resources or event listeners'); recommendations.push('Consider implementing memory profiling'); break; case 'rapid_memory_increase': recommendations.push('Investigate recent operations causing memory spike'); recommendations.push('Consider batch processing for large datasets'); recommendations.push('Review algorithm efficiency'); break; } return recommendations; } /** * GC最適化実行 */ performGCOptimization() { if (global.gc) { try { global.gc(); this.stats.gcOptimizationsPerformed++; this.emit('gc_optimization_performed', { timestamp: Date.now(), memoryBefore: this.getCurrentMemoryUsage() }); if (this.options.verboseLogging) { console.log('♻️ Garbage collection performed'); } } catch (error) { this.emit('error', `GC optimization failed: ${error.message}`); } } } /** * GC実行判定 */ shouldPerformGC(snapshot) { if (!snapshot.deltaFromBaseline) return false; const deltaFromBaselineMB = snapshot.deltaFromBaseline.heapUsed / 1024 / 1024; const recentHistory = this.getRecentHistory(5 * 60 * 1000); // 5分 // 100MB以上増加かつ増加傾向にある場合 return deltaFromBaselineMB > 100 && this.detectMemoryLeakPattern(recentHistory); } /** * 統計更新 */ updateStats(currentMemory) { const heapUsedMB = currentMemory.heapUsed / 1024 / 1024; this.stats.peakMemoryUsage = Math.max(this.stats.peakMemoryUsage, heapUsedMB); // 移動平均でメモリ使用量を更新 const alpha = 0.1; this.stats.averageMemoryUsage = this.stats.averageMemoryUsage * (1 - alpha) + heapUsedMB * alpha; } /** * 履歴サイズ管理 */ manageHistorySize() { if (this.memoryHistory.length > this.options.maxHistorySize) { // 古いデータを削除(50%を残す) const keepSize = Math.floor(this.options.maxHistorySize * 0.5); this.memoryHistory = this.memoryHistory.slice(-keepSize); } } /** * 最近の履歴取得 */ getRecentHistory(timeWindowMs) { const cutoffTime = Date.now() - timeWindowMs; return this.memoryHistory.filter(snapshot => snapshot.timestamp >= cutoffTime); } /** * メモリトレンド分析 */ analyzeMemoryTrend(history) { if (history.length < 2) return null; const first = history[0]; const last = history[history.length - 1]; const timeSpanMs = last.timestamp - first.timestamp; const memoryDeltaMB = (last.memoryUsage.heapUsed - first.memoryUsage.heapUsed) / 1024 / 1024; return { timeSpanMs, memoryDeltaMB, trendRate: (memoryDeltaMB / timeSpanMs) * 60 * 1000, // MB/min sampleCount: history.length }; } /** * 現在のメモリ使用量取得 */ getCurrentMemoryUsage() { const usage = process.memoryUsage(); return { heapUsed: usage.heapUsed, heapTotal: usage.heapTotal, external: usage.external, rss: usage.rss, timestamp: Date.now() }; } /** * 統計取得 */ getStats() { return { ...this.stats, isMonitoring: this.isMonitoring, monitoringDuration: this.startTime ? Date.now() - this.startTime : 0, historySize: this.memoryHistory.length, alertState: this.alertState }; } /** * 詳細レポート生成 */ generateDetailedReport() { const currentMemory = this.getCurrentMemoryUsage(); const report = { timestamp: new Date().toISOString(), monitoringSession: { startTime: this.startTime, duration: this.startTime ? Date.now() - this.startTime : 0, isActive: this.isMonitoring }, currentState: { memoryUsage: currentMemory, alertState: this.alertState }, statistics: this.stats, memoryHistory: this.memoryHistory, trends: this.generateTrendAnalysis(), recommendations: this.generateRecommendations() }; return report; } /** * トレンド分析生成 */ generateTrendAnalysis() { if (this.memoryHistory.length < 10) { return { insufficient_data: true }; } const recentHistory = this.getRecentHistory(30 * 60 * 1000); // 30分 const longTermHistory = this.getRecentHistory(120 * 60 * 1000); // 2時間 return { recentTrend: this.analyzeMemoryTrend(recentHistory), longTermTrend: this.analyzeMemoryTrend(longTermHistory), volatility: this.calculateMemoryVolatility(recentHistory), stability: this.assessMemoryStability(longTermHistory) }; } /** * メモリ変動性計算 */ calculateMemoryVolatility(history) { if (history.length < 3) return 0; const deltas = []; for (let i = 1; i < history.length; i++) { const delta = history[i].memoryUsage.heapUsed - history[i-1].memoryUsage.heapUsed; deltas.push(Math.abs(delta)); } const avgDelta = deltas.reduce((sum, delta) => sum + delta, 0) / deltas.length; return avgDelta / 1024 / 1024; // MB } /** * メモリ安定性評価 */ assessMemoryStability(history) { if (history.length < 5) return 'insufficient_data'; const trend = this.analyzeMemoryTrend(history); const volatility = this.calculateMemoryVolatility(history); if (Math.abs(trend.trendRate) < 1 && volatility < 10) return 'stable'; if (Math.abs(trend.trendRate) < 5 && volatility < 25) return 'moderate'; return 'unstable'; } /** * 推奨事項生成 */ generateRecommendations() { const recommendations = []; const currentMemory = this.getCurrentMemoryUsage(); const heapUsedMB = currentMemory.heapUsed / 1024 / 1024; if (this.stats.alertsTriggered > 5) { recommendations.push({ type: 'frequent_alerts', priority: 'high', message: 'Frequent memory alerts detected. Consider reviewing application memory management.' }); } if (this.stats.peakMemoryUsage > 1000) { recommendations.push({ type: 'high_peak_memory', priority: 'medium', message: 'Peak memory usage exceeds 1GB. Consider implementing streaming or chunked processing.' }); } if (this.stats.gcOptimizationsPerformed > 10) { recommendations.push({ type: 'frequent_gc', priority: 'medium', message: 'Frequent garbage collection optimizations. Review memory allocation patterns.' }); } if (recommendations.length === 0) { recommendations.push({ type: 'healthy', priority: 'info', message: 'Memory usage appears healthy and stable.' }); } return recommendations; } /** * レポート保存 */ async saveReport() { try { const report = this.generateDetailedReport(); const outputPath = path.resolve(this.options.reportOutputFile); await fs.writeFile(outputPath, JSON.stringify(report, null, 2), 'utf8'); this.emit('report_saved', { outputPath, report }); if (this.options.verboseLogging) { console.log(`📊 Memory report saved to: ${outputPath}`); } return outputPath; } catch (error) { this.emit('error', `Failed to save memory report: ${error.message}`); throw error; } } } // デフォルトのメモリモニターインスタンス export const defaultMemoryMonitor = new MemoryMonitor();