sse长连接本身不导致高内存占用,但生命周期管理松散、监听器残留、数据堆积或强引用未释放会引发持续内存增长;需在路由卸载时显式关闭eventsource、避免全局声明、统一工厂封装、精准移除监听器、服务端配合终止推送、节流处理数据并用weakmap避免强引用。

SSE 长连接本身不直接导致高内存占用,但若生命周期管理松散、监听器残留、数据堆积或对象强引用未释放,就会引发持续内存增长——尤其在单页应用频繁切换路由或长时间运行时。核心不是“压缩数据”,而是精准控制连接与引用关系。
路由切换时必须显式关闭 EventSource
EventSource 实例不会随组件卸载自动销毁,浏览器也不会立即回收后台连接。只要没调用 .close(),它就持续接收数据、触发事件、占用内存和带宽。
- React 中,在 useEffect 清理函数里写:
return () => eventSource?.close(); - Vue 3 使用 onBeforeUnmount;Vue 2 对应 beforeDestroy
- 避免将 EventSource 声明在模块顶层或全局作用域,防止跨路由意外复用
防止重复创建与监听器泄漏
快速来回切换页面、重试逻辑不严谨,容易导致多个 EventSource 实例共存,而旧实例因未 close 仍保活。
- 封装统一的 SSE 工厂函数,用 Map 按路径或业务类型缓存实例(如
getSSE('/api/chat')) - 每个实例绑定唯一上下文标识(如
es.__route = '/chat'),便于路由守卫中精准关闭 - 移除监听器时,确保使用同一函数引用:
source.addEventListener('message', handler)→source.removeEventListener('message', handler)
服务端配合终止上游推送
客户端关了,服务端若不知情,仍可能向已断开的连接写入数据,造成 BFF 层连接泄漏、内存堆积、句柄耗尽。
- BFF 层监听
res.on('close')或res.socket.on('close'),触发后立即调用上游请求的 AbortController.abort() - 为每个 SSE 请求添加自定义 header(如
X-Session-ID),服务端据此识别并停止对应流 - 客户端关闭前可发轻量终结请求(如
POST /api/sse/end?sid=xxx),服务端清空会话缓存与推送任务
减少数据堆积与无用强引用
即使连接正常,若前端未及时消费事件或保留了大数据结构的闭包引用,也会阻碍垃圾回收。
- 监听
message事件时,避免在回调中长期持有大对象(如完整日志数组、原始响应体) - 对流式文本做节流处理(如每 100ms 合并一次 chunk),而非逐帧更新 DOM 或状态
- 使用 WeakMap 管理实例与组件的映射关系,避免强引用阻碍 GC
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











