@claude-vector/core
Version:
Core vector search engine for code intelligence
598 lines (510 loc) • 17.7 kB
JavaScript
/**
* 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();