hikaricp的leak-detection-threshold是第一道防线,设为5000–30000ms可触发泄漏告警并精准打印持有连接的代码行号;需确保connection、statement、resultset三层资源全部显式关闭,警惕@transactional中异步操作导致事务上下文未传播而引发的连接长期占用。

Java 中 JDBC 本身不管理连接池,内存泄漏实际是连接池(如 HikariCP、Druid)中连接未归还导致的资源堆积,常被误称为“内存泄漏”。本质是连接对象长期被持有,其关联的 Socket、Statement、ResultSet、驱动内部 PhantomReference 等资源无法释放,最终拖垮堆内存和数据库服务能力。
启用连接泄漏检测(第一道防线)
HikariCP 的 leak-detection-threshold 是最直接有效的手段:
- 在配置中设为 5000–30000ms(测试环境建议 5000,生产可设 30000)
- 触发时日志会精准打印持有连接的代码行号,例如:
WARN - Connection leak detection triggered for connection ... at com.example.dao.UserDao.findUser(UserDao.java:67) - 注意:该行号是 getConnection() 被调用的位置,不是 close 缺失处,但说明该方法内某条路径未归还连接
检查三层资源是否全部关闭
JDBC 规范要求 Connection、Statement、ResultSet 必须显式关闭。只关 Connection 不够,尤其 ResultSet 在 MySQL 中会占用服务端游标:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 错误写法:Connection 手动 close,但 PreparedStatement 和 ResultSet 在 try 外创建 → 后两者不会释放
- 正确写法:用 try-with-resources 声明所有 AutoCloseable 资源:
try (Connection conn = ds.getConnection();<br> PreparedStatement stmt = conn.prepareStatement(sql);<br> ResultSet rs = stmt.executeQuery()) { ... } - 若需复用 Statement 或动态构造 ResultSet,务必确保每个分支(包括 catch/finally)都调用 close()
警惕事务与异步组合陷阱
@Transactional 方法中发起异步操作(如 CompletableFuture、@Async、MQ 发送、Feign 调用),极易引发连接泄漏:
- 事务上下文不传播到新线程,事务无法提交,连接被绑定在线程本地变量中无法释放
- jstack 显示大量 TIMED_WAITING 线程卡在 Thread.sleep 或 OkHttp 的 connectTimeout → 往往就是这类问题
- 修复方式:避免在事务方法内启新线程;必须异步时,用 TransactionSynchronizationManager 卸载事务上下文,或改用非事务连接(如 DataSourceUtils.getConnection(ds, false))
监控与预防性配置
靠事后排查不如提前拦截:
- 在测试/预发环境开启 Actuator + Prometheus,配置告警规则:当 hikaricp_active_connections 持续 > maxPoolSize × 0.8 且持续 2 分钟即告警
- 设置合理 idleTimeout(如 600000ms)和 maxLifetime(如 1800000ms),避免陈旧连接累积
- MySQL 驱动侧注意:老版本 mysql-connector-java 5.x 存在 PhantomReference 泄漏风险(com.mysql.jdbc.NonRegisteringDriver$ConnectionPhantomReference 大量堆积),升级到 8.0.33+ 可缓解
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










