本质是connection未显式释放致连接长期占用,需从代码(try-with-resources/finally)、监控(active≈maxpoolsize)、配置(hikaricp leak-detection-threshold)三方面排查,重点验证异常路径关闭、泄漏检测日志及事务边界。

Java 中数据库连接未关闭导致连接池耗尽,本质是 Connection 对象未被显式释放,造成连接长期占用、无法归还池中。排查需从代码逻辑、监控指标和配置三方面入手,重点确认连接是否在所有路径(包括异常分支)下都被 close()。
检查代码中 Connection 的使用是否遵循 try-with-resources 或显式 close()
最常见的问题是:只在正常流程中 close(),但 catch 块或 finally 块缺失,或用了错误的 close 顺序(如先 close Statement 再 close Connection,但 Statement.close() 抛异常导致 Connection 没机会 close)。
- 优先用 try-with-resources(JDK 7+),它能自动保证 close,即使发生异常:
PreparedStatement stmt = conn.prepareStatement("SELECT ...");
ResultSet rs = stmt.executeQuery()) {
// 处理结果
}
- 若手动管理,必须在 finally 块中 close,且每个资源单独 try-catch(避免一个 close 失败阻断后续):
try {
conn = dataSource.getConnection();
// ...
} finally {
if (conn != null) try { conn.close(); } catch (SQLException ignored) {}
}
启用连接池的泄漏检测与日志
主流连接池(HikariCP、Druid、Tomcat JDBC)都支持连接泄漏追踪,可定位“谁拿了连接没还”。
-
HikariCP:设置
leakDetectionThreshold(单位毫秒),例如 60000 表示空闲超 60 秒未归还就记录警告日志,并打印当前线程堆栈:
-
Druid:开启
removeAbandonedOnBorrow=true和removeAbandonedTimeoutMillis=60000,并设logAbandoned=true输出泄露堆栈; - 观察日志中类似 “abandoned connection” 或 “connection leak detected” 的条目,重点关注其 stack trace 中的业务类和方法名。
监控连接池实时状态与历史趋势
仅靠日志不够,需结合运行时指标判断是否真耗尽、何时开始恶化。
- 查看连接池关键指标:active(正在使用的连接数)≈ maxPoolSize 且长时间不降,是典型征兆;
- HikariCP 提供 JMX 或 Actuator 端点(
/actuator/metrics/hikaricp.connections.active); - Druid 内置监控页面(
/druid/index.html),可直观看到活跃连接数、等待线程数、SQL 执行明细; - 配合 APM 工具(如 SkyWalking、Pinpoint)追踪慢 SQL 或长事务,它们常是连接不释放的诱因。
验证事务边界与框架集成是否隐式持有了连接
Spring 的 @Transactional、MyBatis 的 SqlSession、JPA 的 EntityManager 都可能延长连接生命周期,容易被忽略。
- 确认 @Transactional 方法是否过长、是否嵌套了未提交的子事务;
- MyBatis 使用
SqlSessionTemplate时,确保没有手动调用openSession()后忘记 close; - JPA 中避免在事务外持有 EntityManager 引用,或用
EntityManager.unwrap(Connection.class)后未归还; - 检查是否有线程池任务中获取连接但未在任务结束前释放(比如异步处理中忘了 close)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











