diff --git a/js/agents/core/event-bus-subscriptions.js b/js/agents/core/event-bus-subscriptions.js index 77e2c589..b54b6a76 100644 --- a/js/agents/core/event-bus-subscriptions.js +++ b/js/agents/core/event-bus-subscriptions.js @@ -224,6 +224,41 @@ export class EventBusSubscriptions { return handlers; } + /** + * 统计指定事件类型的订阅数量 + * @param {string} eventType - 事件类型 + * @returns {number} 订阅数量 + */ + count(eventType) { + let total = 0; + + // 精确匹配 - 普通 + const direct = this._listeners.get(eventType); + if (direct) total += direct.size; + + // 精确匹配 - 优先级 + const priorityMap = this._priorityListeners.get(eventType); + if (priorityMap) { + for (const set of priorityMap.values()) { + total += set.size; + } + } + + // 通配符 - 普通 + const wildcardSet = this._wildcardListeners.get(eventType); + if (wildcardSet) total += wildcardSet.size; + + // 通配符 - 优先级 + const wildcardPriorityMap = this._wildcardPriorityListeners.get(eventType); + if (wildcardPriorityMap) { + for (const set of wildcardPriorityMap.values()) { + total += set.size; + } + } + + return total; + } + /** * @returns {void} */ diff --git a/js/agents/core/event-bus.js b/js/agents/core/event-bus.js index 7bbb7370..102c9339 100644 --- a/js/agents/core/event-bus.js +++ b/js/agents/core/event-bus.js @@ -245,13 +245,25 @@ export class EventBus { /** * 高级订阅(支持优先级和 AbortSignal) + * + * ⚠️ 内存泄漏风险:必须手动调用返回的 unsubscribe 函数以释放监听器。 + * 未调用 unsubscribe 会导致监听器累积,造成内存泄漏。 + * * @param {string} eventType - 事件类型 * @param {EventHandler} handler - 处理函数 * @param {{ priority?: number, signal?: AbortSignal }} [options] - { priority, signal } * @returns {() => void} 取消订阅函数 */ subscribe(eventType, handler, options = {}) { - return this._subscriptions.subscribe(eventType, handler, options); + const unsub = this._subscriptions.subscribe(eventType, handler, options); + + // 监控订阅数,检测潜在泄漏 + const count = this._subscriptions.count(eventType); + if (count > 100) { + logger.warn(`High subscription count for ${eventType}: ${count}. Potential memory leak - ensure unsubscribe is called.`); + } + + return unsub; } /**