redisson localcachedmap 内存泄漏主因是同步资源未释放、监听器未注销、广播消息堆积及淘汰策略缺陷,表现为bytebuf等对象持续增长;修复需升级版本、调用destroy()、禁用重试并规范缓存对象。

Redisson 的 LocalCachedMap 在启用本地缓存时,若同步机制配置或使用不当,确实会引发内存泄漏——核心问题不在“缓存数据本身”,而在于**同步过程产生的中间资源未释放、监听器未注销、或广播消息堆积**。这类泄漏往往隐蔽,表现为堆内存中 ByteBuf、PubSubConnectionEntry、RedissonLockEntry 等对象持续增长,GC 无法回收。
同步广播未清理导致的 ByteBuf 泄漏
当多个节点通过 LocalCachedMap 更新数据时,Redisson 默认使用 Redis 的 Pub/Sub 机制广播失效事件(如 INVALIDATE)。在旧版本(如 3.17.x–3.18.0)中,putAll() 或 fastRemove() 后触发的序列化/反序列化流程中,JsonJacksonCodec 创建的 ByteBuf 在异常路径下未被显式 release(),最终被 Netty 内存检测器标记为泄漏。
- 典型日志:
LEAK: ByteBuf.release() was not called before it's garbage-collected - 高发场景:高频批量写 + 本地缓存开启 + 同步策略为
UPDATE或INVALIDATE - 修复方式:升级至 Redisson 3.22.0+(已修复 codec 中所有分支的
ByteBuf释放逻辑)
监听器长期驻留引发的引用泄漏
LocalCachedMap 内部依赖 RTopic 订阅缓存变更事件。若应用动态创建 RLocalCachedMap 实例但未显式注销监听器,或在 Spring 环境中 Bean 生命周期管理不严谨,会导致:
- 每个 map 实例绑定一个独立的
RTopic监听器 - 监听器持有对 map、codec、连接上下文的强引用
- 即使 map 对象被回收,监听器仍驻留在 Pub/Sub 连接池中,持续接收并堆积消息
解决方案包括:
— 使用 map.destroy() 主动销毁本地缓存实例(该方法会自动取消订阅)
— 避免在循环或方法内反复调用 client.getLocalCachedMap() 创建新实例
— 在 Spring 中确保 @PreDestroy 或 DisposableBean 中调用 destroy()
重连策略与失效消息积压
当网络抖动或 Redis 节点临时不可用时,LocalCachedMap 的失效广播可能失败。若配置了 ReconnectionStrategy.CLEAR 或 .RELIABLE,重连后会尝试重发积压消息;而某些版本中,这些待重发消息体(含未释放的 ByteBuf)会被缓存在内存队列中,长期不消费即形成泄漏。
- 检查项:
LocalCachedMapOptions.reconnectionStrategy()是否设为NONE(默认),避免自动重试堆积 - 建议:生产环境优先使用
SyncStrategy.INVALIDATE+ReconnectionStrategy.NONE,由业务层控制重试边界 - 监控指标:关注
redisson_pubsub_messages_received_total和redisson_localcache_eviction_count是否失衡
本地缓存淘汰策略与对象引用残留
当设置 evictionPolicy(LFU) 或 LRU 且 cacheSize > 0 时,Redisson 会维护访问频次/时间索引结构。若缓存 value 是大对象(如未序列化的 POJO、含内部引用的集合),而 key 使用了非标准 equals/hashCode 实现,可能导致:
- 淘汰逻辑无法准确定位待移除条目
- 已失效的 entry 仍保留在 LRU 链表或 LFU 计数器中
- 间接阻止 value 对象被 GC
规避方式:
— 缓存 value 统一使用可序列化轻量对象(如 String、byte[]、DTO)
— 避免将 Spring Bean、Connection、ThreadLocal 等上下文对象放入本地缓存
— 如必须缓存复杂对象,确认其 hashCode() 稳定且 equals() 正确实现
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











