| |
| |
| |
| |
| |
| |
|
|
| import logger from './logger.js'; |
| import { GC_COOLDOWN } from '../constants/index.js'; |
|
|
| |
| |
| |
| |
| const MemoryPressure = { |
| LOW: 'low', |
| MEDIUM: 'medium', |
| HIGH: 'high', |
| CRITICAL: 'critical' |
| }; |
|
|
| |
| |
| |
| |
| |
| function calculateThresholds(thresholdMB) { |
| const highBytes = thresholdMB * 1024 * 1024; |
| return { |
| LOW: Math.floor(highBytes * 0.3), |
| MEDIUM: Math.floor(highBytes * 0.6), |
| HIGH: highBytes, |
| TARGET: Math.floor(highBytes * 0.5) |
| }; |
| } |
|
|
| |
| let THRESHOLDS = calculateThresholds(100); |
|
|
| |
| const POOL_SIZES = { |
| [MemoryPressure.LOW]: { chunk: 30, toolCall: 15, lineBuffer: 5 }, |
| [MemoryPressure.MEDIUM]: { chunk: 20, toolCall: 10, lineBuffer: 3 }, |
| [MemoryPressure.HIGH]: { chunk: 10, toolCall: 5, lineBuffer: 2 }, |
| [MemoryPressure.CRITICAL]: { chunk: 5, toolCall: 3, lineBuffer: 1 } |
| }; |
|
|
| |
| |
| |
| class MemoryManager { |
| constructor() { |
| |
| this.currentPressure = MemoryPressure.LOW; |
| |
| this.cleanupCallbacks = new Set(); |
| |
| this.lastGCTime = 0; |
| |
| this.gcCooldown = GC_COOLDOWN; |
| this.checkInterval = null; |
| this.isShuttingDown = false; |
| |
| this.configuredThresholdMB = 100; |
| |
| |
| this.stats = { |
| gcCount: 0, |
| cleanupCount: 0, |
| peakMemory: 0 |
| }; |
| } |
|
|
| |
| |
| |
| |
| setThreshold(thresholdMB) { |
| if (thresholdMB && thresholdMB > 0) { |
| this.configuredThresholdMB = thresholdMB; |
| THRESHOLDS = calculateThresholds(thresholdMB); |
| logger.info(`内存阈值已设置: ${thresholdMB}MB (LOW: ${Math.floor(THRESHOLDS.LOW/1024/1024)}MB, MEDIUM: ${Math.floor(THRESHOLDS.MEDIUM/1024/1024)}MB, HIGH: ${Math.floor(THRESHOLDS.HIGH/1024/1024)}MB)`); |
| } |
| } |
|
|
| |
| |
| |
| getThresholds() { |
| return { |
| configuredMB: this.configuredThresholdMB, |
| lowMB: Math.floor(THRESHOLDS.LOW / 1024 / 1024), |
| mediumMB: Math.floor(THRESHOLDS.MEDIUM / 1024 / 1024), |
| highMB: Math.floor(THRESHOLDS.HIGH / 1024 / 1024), |
| targetMB: Math.floor(THRESHOLDS.TARGET / 1024 / 1024) |
| }; |
| } |
|
|
| |
| |
| |
| |
| start(interval = 30000) { |
| if (this.checkInterval) return; |
| |
| this.checkInterval = setInterval(() => { |
| if (!this.isShuttingDown) { |
| this.check(); |
| } |
| }, interval); |
| |
| |
| this.check(); |
| logger.info(`内存管理器已启动 (检查间隔: ${interval/1000}秒)`); |
| } |
|
|
| |
| |
| |
| stop() { |
| this.isShuttingDown = true; |
| if (this.checkInterval) { |
| clearInterval(this.checkInterval); |
| this.checkInterval = null; |
| } |
| this.cleanupCallbacks.clear(); |
| logger.info('内存管理器已停止'); |
| } |
|
|
| |
| |
| |
| |
| registerCleanup(callback) { |
| this.cleanupCallbacks.add(callback); |
| } |
|
|
| |
| |
| |
| |
| unregisterCleanup(callback) { |
| this.cleanupCallbacks.delete(callback); |
| } |
|
|
| |
| |
| |
| getMemoryUsage() { |
| const usage = process.memoryUsage(); |
| return { |
| heapUsed: usage.heapUsed, |
| heapTotal: usage.heapTotal, |
| rss: usage.rss, |
| external: usage.external, |
| heapUsedMB: Math.round(usage.heapUsed / 1024 / 1024 * 10) / 10 |
| }; |
| } |
|
|
| |
| |
| |
| getPressureLevel(heapUsed) { |
| if (heapUsed < THRESHOLDS.LOW) return MemoryPressure.LOW; |
| if (heapUsed < THRESHOLDS.MEDIUM) return MemoryPressure.MEDIUM; |
| if (heapUsed < THRESHOLDS.HIGH) return MemoryPressure.HIGH; |
| return MemoryPressure.CRITICAL; |
| } |
|
|
| |
| |
| |
| getPoolSizes() { |
| return POOL_SIZES[this.currentPressure]; |
| } |
|
|
| |
| |
| |
| getCurrentPressure() { |
| return this.currentPressure; |
| } |
|
|
| |
| |
| |
| check() { |
| const { heapUsed, heapUsedMB } = this.getMemoryUsage(); |
| const newPressure = this.getPressureLevel(heapUsed); |
| |
| |
| if (heapUsed > this.stats.peakMemory) { |
| this.stats.peakMemory = heapUsed; |
| } |
| |
| |
| if (newPressure !== this.currentPressure) { |
| logger.info(`内存压力变化: ${this.currentPressure} -> ${newPressure} (${heapUsedMB}MB)`); |
| this.currentPressure = newPressure; |
| } |
| |
| |
| switch (newPressure) { |
| case MemoryPressure.CRITICAL: |
| this.handleCriticalPressure(heapUsedMB); |
| break; |
| case MemoryPressure.HIGH: |
| this.handleHighPressure(heapUsedMB); |
| break; |
| case MemoryPressure.MEDIUM: |
| this.handleMediumPressure(heapUsedMB); |
| break; |
| |
| } |
| |
| return newPressure; |
| } |
|
|
| |
| |
| |
| handleMediumPressure(heapUsedMB) { |
| |
| this.notifyCleanup(MemoryPressure.MEDIUM); |
| this.stats.cleanupCount++; |
| } |
|
|
| |
| |
| |
| handleHighPressure(heapUsedMB) { |
| logger.info(`内存较高 (${heapUsedMB}MB),执行积极清理`); |
| this.notifyCleanup(MemoryPressure.HIGH); |
| this.stats.cleanupCount++; |
| |
| |
| this.tryGC(); |
| } |
|
|
| |
| |
| |
| handleCriticalPressure(heapUsedMB) { |
| logger.warn(`内存紧急 (${heapUsedMB}MB),执行紧急清理`); |
| this.notifyCleanup(MemoryPressure.CRITICAL); |
| this.stats.cleanupCount++; |
| |
| |
| this.forceGC(); |
| } |
|
|
| |
| |
| |
| notifyCleanup(pressure) { |
| for (const callback of this.cleanupCallbacks) { |
| try { |
| callback(pressure); |
| } catch (error) { |
| logger.error('清理回调执行失败:', error.message); |
| } |
| } |
| } |
|
|
| |
| |
| |
| tryGC() { |
| const now = Date.now(); |
| if (now - this.lastGCTime < this.gcCooldown) { |
| return false; |
| } |
| return this.forceGC(); |
| } |
|
|
| |
| |
| |
| forceGC() { |
| if (global.gc) { |
| const before = this.getMemoryUsage().heapUsedMB; |
| global.gc(); |
| this.lastGCTime = Date.now(); |
| this.stats.gcCount++; |
| const after = this.getMemoryUsage().heapUsedMB; |
| logger.info(`GC 完成: ${before}MB -> ${after}MB (释放 ${(before - after).toFixed(1)}MB)`); |
| return true; |
| } |
| return false; |
| } |
|
|
| |
| |
| |
| cleanup() { |
| return this.check(); |
| } |
|
|
| |
| |
| |
| getStats() { |
| const memory = this.getMemoryUsage(); |
| return { |
| ...this.stats, |
| currentPressure: this.currentPressure, |
| currentHeapMB: memory.heapUsedMB, |
| peakMemoryMB: Math.round(this.stats.peakMemory / 1024 / 1024 * 10) / 10, |
| poolSizes: this.getPoolSizes(), |
| thresholds: this.getThresholds() |
| }; |
| } |
| } |
|
|
| |
| const memoryManager = new MemoryManager(); |
| export default memoryManager; |
|
|
| |
| export function registerMemoryPoolCleanup(pool, getMaxSize) { |
| memoryManager.registerCleanup(() => { |
| const maxSize = getMaxSize(); |
| while (pool.length > maxSize) { |
| pool.pop(); |
| } |
| }); |
| } |
| export { MemoryPressure, THRESHOLDS }; |