Redisson 内存泄漏排查方法

1. 确认内存泄漏现象
  • 使用 JVM 监控工具(如 VisualVM、JConsole)观察堆内存变化
  • 监控指标:
    • 老年代(Old Gen)持续增长
    • Full GC 后内存不释放
    • OOM 错误日志
  • Redisson 特有指标:通过 redisson.getNodesGroup().pingAll() 检查连接状态
2. 堆转储分析
# 生成堆转储文件
jmap -dump:live,format=b,file=redisson_dump.hprof <pid>

  • 使用 Eclipse MATJProfiler 分析:
    1. 查找 org.redisson 开头的对象实例
    2. 检查 RMapCache/RBucket 等缓存对象是否异常堆积
    3. 定位 ReferenceQueue 未释放的对象
3. 连接池泄漏检测
// 检查连接池状态
Config config = redisson.getConfig();
ConnectionManager manager = config.getConnectionManager();
System.out.println("Active connections: " 
                  + manager.getConnectionPool().getActiveConnections());

  • 异常情况:连接数持续增长不释放
  • 解决方案:检查 try-with-resources 使用情况
    try (RLock lock = redisson.getLock("key")) {
        lock.lock();
        // 业务逻辑
    } // 自动释放
    

4. 监听器泄漏排查
// 事件监听器需显式移除
int listenerId = topic.addListener(MessageListener.class, (msg) -> {...});
// 业务完成后移除
topic.removeListener(listenerId); 

  • 常见泄漏点:
    • 未移除的 RTopic 消息监听器
    • RMap.listIterator() 未关闭的迭代器
    • RKeys.getKeysByPattern() 未关闭的游标
5. 缓存策略检查
  • 验证缓存配置:
    RMapCache<String, Object> map = redisson.getMapCache("cache");
    map.put("key", value, 10, TimeUnit.MINUTES); // 必须设置TTL
    

  • 泄漏场景:
    • 未设置 maxIdleTime 的本地缓存
    • RMapCache 未启用 evictionPolicy
6. 线程泄漏检测
// 检查Redisson内部线程
Set<Thread> threads = Thread.getAllStackTraces().keySet();
threads.stream()
       .filter(t -> t.getName().contains("redisson"))
       .forEach(System.out::println);

  • 重点关注:
    • redisson-netty-* 线程持续增长
    • commandExecutor 线程未关闭
7. 版本与配置验证
配置项 推荐值 泄漏风险
idleTimeout 30000 ms 超时未释放导致堆积
connectionPoolSize 64 (生产环境) 过小引发阻塞泄漏
watchdogTimeout 30000 ms 锁续期失败导致死锁
8. 日志分析技巧
# 启用Redisson调试日志
-Dorg.redisson.level=DEBUG

  • 关键日志模式:
    • DEBUG o.r.c.pool.MasterConnectionPool - free connection released
    • WARN o.r.c.pool.ConnectionPool - connection not received
9. 代码审查要点
// 高危模式示例
public void leakMethod() {
    RMap map = redisson.getMap("leak"); // ① 未关闭
    map.put("k", new byte[1024*1024]);   // ② 大对象
}

  • 必须审查:
    1. try-finally 缺失的资源释放
    2. 静态集合持有 Redisson 对象
    3. 未清理的 RScoredSortedSet 过期元素
10. 压测验证
// 使用JMH模拟泄漏场景
@Benchmark
public void testLockLeak(Blackhole bh) {
    RLock lock = redisson.getLock("bench");
    lock.lock();
    bh.consume(lock); // 不释放锁
}

  • 监控项:
    • jstat -gcutil <pid> 1000
    • jcmd <pid> VM.native_memory

终极解决方案:当定位到特定对象泄漏时,通过弱引用包装

WeakReference<RMap> weakMap = new WeakReference<>(redisson.getMap("safe"));

通过以上步骤,90%的Redisson内存泄漏问题可被定位。重点优先检查连接池、监听器和缓存配置,这些是最高频的泄漏根源。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐