Java JDBC连接泄漏本质是Connection、Statement、ResultSet任一未释放,导致连接长期占用;需通过HikariCP的leak-detection-threshold日志定位行号,结合jstack区分线程状态,检查三者关闭完整性及try-with-resources写法。

Java 中 JDBC 连接泄漏隐患,本质是 Connection、Statement、ResultSet 三者中任一未被显式或自动释放,导致连接长期被占用、资源无法复用。排查重点不在“有没有报错”,而在“借了没还”和“关了没真放”。关键要结合配置、日志、运行态和代码四层交叉验证。
看连接池监控,确认是否真泄漏
别等服务崩了才动手。先查实时指标:
- HikariCP:检查 active(活跃)、idle(空闲)、total(总数)三数——若 active 持续接近 total 且 idle 长期为 0,就是典型泄漏信号
- Druid:关注「活动连接数」和「等待线程数」,后者持续上升说明新请求拿不到连接
- 数据库端佐证:MySQL 执行
SHOW STATUS LIKE 'Threads_connected';,数值远超连接池最大值(如池设 20,DB 显示 200+),基本坐实泄漏
开泄漏检测,直接定位到行号
这是最高效的第一步,无需改代码就能拿到堆栈:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- HikariCP 加配置:
spring.datasource.hikari.leak-detection-threshold=30000(单位毫秒),建议从 30 秒起步,覆盖正常业务但能捕获异常滞留 - Druid 开启:
removeAbandonedOnBorrow=true+removeAbandonedTimeout=60+logAbandoned=true - 触发后日志会打印类似:WARN - Connection leak detection triggered for connection … at com.xxx.service.UserService.query(UserService.java:45)——这行就是泄漏源头,不用猜
查代码写法,盯住三类高危模式
90% 的泄漏藏在老式写法里,重点扫这些位置:
立即学习“Java免费学习笔记(深入)”;
-
手动获取 + 忘记 close:写成
conn = ds.getConnection(); ... conn.close();,中间一旦抛异常,close 就跳过了 - 分支遗漏:if/else 或多个 return 场景下,只在某一分支 close,其他路径直接 return
-
try-with-resources 使用不全:只写了
try (Connection c) {...},但 Statement 和 ResultSet 在 try 外声明或返回,它们不会被自动关闭
正确写法必须把三者都纳入同一 try 资源声明:
try (Connection c = ds.getConnection();<br>
PreparedStatement ps = c.prepareStatement(sql);<br>
ResultSet rs = ps.executeQuery()) { ... }
抓运行态线索,区分真泄漏和假拥堵
有些“满”不是泄漏,而是慢 SQL 或长事务卡住连接:
- 用
jstack <pid>看线程状态:
• 大量 RUNNABLE 卡在socketRead0→ 很可能 SQL 执行中挂起,没走到 close
• 多个 TIMED_WAITING 卡在Thread.sleep或 HTTP 调用 → 事务未提交,连接一直被 hold - 查 MySQL
SHOW PROCESSLIST;:
• 看大量连接状态为 Sleep 且 Time 超过 600 秒 → 典型借出未还
• 若状态为 Executing 且 Time 很长 → 是慢 SQL 占着连接,不是泄漏

















