File size: 8,590 Bytes
0fadeb3
 
 
cda8be4
4bf0fdd
0fadeb3
 
 
cda8be4
0fadeb3
4bf0fdd
 
 
 
0fadeb3
cda8be4
 
 
 
0fadeb3
 
cda8be4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0fadeb3
 
 
 
 
 
 
 
 
4bf0fdd
 
 
0fadeb3
 
4bf0fdd
0fadeb3
4bf0fdd
0fadeb3
4bf0fdd
0fadeb3
4bf0fdd
 
0fadeb3
 
cda8be4
 
0fadeb3
 
 
 
 
 
 
 
 
cda8be4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0fadeb3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cda8be4
 
0fadeb3
 
 
 
 
 
 
873f10f
 
 
 
 
 
 
 
 
 
0fadeb3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
/**
 * 智能内存管理器
 * 采用分级策略,根据内存压力动态调整缓存和对象池
 * 阈值基于用户配置的 memoryThreshold(MB)动态计算
 * @module utils/memoryManager
 */

import logger from './logger.js';
import { GC_COOLDOWN } from '../constants/index.js';

/**
 * 内存压力级别枚举
 * @enum {string}
 */
const MemoryPressure = {
  LOW: 'low',       // < 30% 阈值 - 正常运行
  MEDIUM: 'medium', // 30%-60% 阈值 - 轻度清理
  HIGH: 'high',     // 60%-100% 阈值 - 积极清理
  CRITICAL: 'critical' // > 100% 阈值 - 紧急清理
};

/**
 * 根据用户配置的内存阈值计算各级别阈值
 * @param {number} thresholdMB - 用户配置的内存阈值(MB),即高压力阈值
 * @returns {Object} 各级别阈值(字节)
 */
function calculateThresholds(thresholdMB) {
  const highBytes = thresholdMB * 1024 * 1024;
  return {
    LOW: Math.floor(highBytes * 0.3),      // 30% 为低压力阈值
    MEDIUM: Math.floor(highBytes * 0.6),   // 60% 为中等压力阈值
    HIGH: highBytes,                        // 100% 为高压力阈值(用户配置值)
    TARGET: Math.floor(highBytes * 0.5)    // 50% 为目标内存
  };
}

// 默认阈值(100MB),会在初始化时被配置覆盖
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() {
    /** @type {string} */
    this.currentPressure = MemoryPressure.LOW;
    /** @type {Set<Function>} */
    this.cleanupCallbacks = new Set();
    /** @type {number} */
    this.lastGCTime = 0;
    /** @type {number} */
    this.gcCooldown = GC_COOLDOWN;
    this.checkInterval = null;
    this.isShuttingDown = false;
    /** @type {number} 用户配置的内存阈值(MB) */
    this.configuredThresholdMB = 100;
    
    // 统计信息
    this.stats = {
      gcCount: 0,
      cleanupCount: 0,
      peakMemory: 0
    };
  }

  /**
   * 设置内存阈值(从配置加载)
   * @param {number} thresholdMB - 内存阈值(MB)
   */
  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)
    };
  }

  /**
   * 启动内存监控
   * @param {number} interval - 检查间隔(毫秒)
   */
  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('内存管理器已停止');
  }

  /**
   * 注册清理回调
   * @param {Function} callback - 清理函数,接收 pressure 参数
   */
  registerCleanup(callback) {
    this.cleanupCallbacks.add(callback);
  }

  /**
   * 取消注册清理回调
   * @param {Function} 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;
      // LOW 压力不需要特殊处理
    }
    
    return newPressure;
  }

  /**
   * 处理中等压力
   */
  handleMediumPressure(heapUsedMB) {
    // 通知各模块缩减对象池
    this.notifyCleanup(MemoryPressure.MEDIUM);
    this.stats.cleanupCount++;
  }

  /**
   * 处理高压力
   */
  handleHighPressure(heapUsedMB) {
    logger.info(`内存较高 (${heapUsedMB}MB),执行积极清理`);
    this.notifyCleanup(MemoryPressure.HIGH);
    this.stats.cleanupCount++;
    
    // 尝试触发 GC(带冷却)
    this.tryGC();
  }

  /**
   * 处理紧急压力
   */
  handleCriticalPressure(heapUsedMB) {
    logger.warn(`内存紧急 (${heapUsedMB}MB),执行紧急清理`);
    this.notifyCleanup(MemoryPressure.CRITICAL);
    this.stats.cleanupCount++;
    
    // 强制 GC(忽略冷却)
    this.forceGC();
  }

  /**
   * 通知所有注册的清理回调
   */
  notifyCleanup(pressure) {
    for (const callback of this.cleanupCallbacks) {
      try {
        callback(pressure);
      } catch (error) {
        logger.error('清理回调执行失败:', error.message);
      }
    }
  }

  /**
   * 尝试触发 GC(带冷却时间)
   */
  tryGC() {
    const now = Date.now();
    if (now - this.lastGCTime < this.gcCooldown) {
      return false;
    }
    return this.forceGC();
  }

  /**
   * 强制触发 GC
   */
  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 };