OOM 是 Error,非 Exception,一旦发生应用基本不可用;高并发秒杀时本地内存存 Session 无过期机制,导致对象堆积、GC 压力大、内存持续增长直至 OOM。

高并发秒杀场景下,将用户 Session 直接存入 JVM 本地内存(如 ConcurrentHashMap)且未设置过期机制,极易引发内存持续增长、GC 压力飙升,最终导致 OutOfMemoryError(OOM)。
Session 本地内存堆积的典型原因
秒杀活动期间瞬时流量激增,大量用户发起请求并创建 Session;若每个 Session 都以对象形式长期驻留堆内存,又无主动清理策略:
- Session 对象本身(含用户ID、商品ID、时间戳、状态等)占用堆空间
- 未绑定 GC 可达性生命周期,JVM 无法自动回收
- 缓存未设最大容量或 LRU/LFU 淘汰逻辑,内存只增不减
- 部分实现还保存了临时订单、预占库存等中间状态,体积更大
为什么不能依赖本地内存存 Session
本地内存本质是单机有状态存储,与高并发、可伸缩、容错等需求天然冲突:
- 集群部署时,用户请求可能被负载均衡到任意节点,Session 无法共享 → 出现“登录失效”“重复下单”等问题
- 服务重启或扩容缩容时,内存中 Session 全量丢失,影响业务连续性
- OOM 后 JVM 进程崩溃,整个实例不可用,放大故障影响面
- 堆内存监控难定位具体哪个 Session 占用高,排查成本高
推荐的轻量级替代方案
放弃本地内存 Session,改用外部、可控、带过期能力的存储:
抓取并分析 OpenClaw JSONL 会话日志,重建并回填代理记忆文件。适用于:(1) 模型切换后记忆不完整,(2) 验证记忆覆盖度,(3) 重建丢失记忆,(4) 通过 cron/heartbeat 自动同步每日记忆。支持简单提取及基于 LLM 的叙事摘要,并自动清理敏感信息。
立即学习“Java免费学习笔记(深入)”;
-
Redis + Token 模式:登录后生成唯一 token,Session 数据全量存 Redis,设置
EXPIRE(如 30 分钟),key 命名为session:{token} - JWT 无状态化:将必要用户信息(非敏感)编码进 JWT,签名防篡改,服务端无需存储 Session,校验即用
- Redis Hash 分片存储:对高频访问字段(如用户ID、秒杀资格)单独建 Hash 结构,配合 TTL 精细控制生命周期
-
加内存回收兜底:若仍需本地缓存(如热点用户信息),必须配
Guava Cache或Caffeine,强制设置expireAfterWrite和maximumSize
快速自查与修复建议
检查现有代码中是否存在类似以下危险模式:
-
private static final Map<String, Session> sessionStore = new ConcurrentHashMap<>();—— 无清理逻辑 -
sessionStore.put(userId, new Session(...));—— 写入不设限、不设过期 - JVM 启动参数未调优(如
-Xmx过大掩盖问题,或过小加速 OOM)
修复时优先迁移至 Redis 存储,并在网关层统一校验 token 有效性,后端服务彻底无状态。秒杀核心链路应避免任何本地内存缓存用户上下文。

















