WebSocket长连接会话对象(Session)未及时移除必然导致内存堆积。必须在@OnClose中显式清理Session,避免静态Map强引用,并配合心跳超时、弱引用等兜底机制确保彻底释放。

Java中WebSocket长连接会话对象(Session)未及时移除,是后端内存堆积的典型诱因。这不是“可能泄漏”,而是必然积累——只要Session没从管理容器中显式清理,它连同其持有的用户数据、上下文引用、监听器、缓冲区等,就一直被强引用驻留在堆中,GC无法回收。
Session管理不闭环,等于给内存挖坑
WebSocket连接断开(无论是客户端主动关闭、网络中断还是服务端踢出),@OnClose方法必须执行清理逻辑。但很多代码只做了“业务层面的下线通知”,却忘了真正释放Session本身。尤其当Session被存入静态Map、ConcurrentHashMap或自定义SessionManager中时,若没有配套的remove操作,每断一个连接就多留一个“幽灵Session”。
Session残留的常见表现
- JVM堆内存持续上涨,Full GC频率升高,但每次回收效果差(GC日志显示“only 1.2% freed”)
- 使用jmap -histo
查看, javax.websocket.Session或org.apache.tomcat.websocket.WsSession实例数远超当前在线用户数 - MAT分析堆转储(heap dump),发现Session对象的Retained Heap异常大,且被静态集合(如
RoomManager.sessions)直接持有 - Session内部持有了大量业务对象(如用户DTO、编辑状态快照、临时缓存),形成级联引用链
关键修复动作清单
抓取并分析 OpenClaw JSONL 会话日志,重建并回填代理记忆文件。适用于:(1) 模型切换后记忆不完整,(2) 验证记忆覆盖度,(3) 重建丢失记忆,(4) 通过 cron/heartbeat 自动同步每日记忆。支持简单提取及基于 LLM 的叙事摘要,并自动清理敏感信息。
- 在
@OnClose回调中,必须调用SessionManager.remove(session),确保从所有存储结构中彻底移除该Session - 避免将Session直接put进静态Map;如需索引,优先使用WeakHashMap(以Session为key,自动随Session GC而失效),或配合引用队列做兜底清理
- 若Session关联了定时任务(如心跳检测、自动踢人)、线程池任务、异步回调,务必在remove前取消这些资源,否则它们仍会隐式持有Session引用
- 对Room/Channel等聚合容器,采用“弱引用+清理钩子”双保险:例如在Session关闭时,不仅从room.sessions移除,还触发room.broadcast("user_left")并清空该用户专属的消息队列
- 启用WebSocket配置项
server.websocket.max-text-message-size和max-binary-message-size,防止恶意或异常大消息撑爆单个Session缓冲区
兜底机制不能少
仅依赖@OnClose不可靠——客户端强制关闭、网络闪断、浏览器崩溃等情况不会触发该回调。建议叠加:
- 实现心跳超时机制:服务端记录每个Session最后ping时间,后台线程定期扫描,对超时Session主动调用
session.close()并执行清理 - 利用Tomcat/Jetty的
SessionListener或Spring WebSocket的HandshakeInterceptor.afterHandshake与SessionDisconnectEvent事件,建立统一生命周期监听入口
不复杂但容易忽略。真正的难点不在“关连接”,而在“清干净”。Session一旦漏删,它拖着的不只是自己,还有整个协作上下文。
立即学习“Java免费学习笔记(深入)”;

















