数据库连接未关闭的本质是长期持有Connection等对象,间接占用网络缓冲区、事务上下文等堆外资源,引发连接池耗尽或OOM;修复核心是100%归还连接,优先用try-with-resources自动释放,次选手动finally按ResultSet→Statement→Connection顺序逐层关闭并捕获异常。

Java中数据库连接未关闭,本质不是直接占大量堆内存,而是让Connection、Statement、ResultSet这些对象长期存活,间接拖住底层JDBC驱动、网络缓冲区、事务上下文等资源,最终引发连接池耗尽、GC压力上升甚至OOM。修复核心就一条:确保每次获取的连接在使用后100%归还。
优先用 try-with-resources 自动释放
这是最可靠、最简洁的方式,要求所有资源实现 AutoCloseable 接口(JDBC 4.0+ 的 Connection、Statement、ResultSet 都满足):
- 把 Connection、PreparedStatement、ResultSet 全部声明在 try 括号内,JVM 会在 try 块退出时(无论正常结束还是抛异常)自动调用 close()
- 不需要手写 finally,也不用判空或嵌套 try-catch 关闭逻辑,代码更清晰、容错更强
- 注意:多个资源用分号隔开,关闭顺序与声明顺序相反(先关 ResultSet,再 Statement,最后 Connection)
不用 try-with-resources 时,必须用 finally 完整关闭
如果因兼容旧版本或特殊框架限制无法使用 try-with-resources,那就只能手动保障关闭流程:
- 关闭顺序不能错:ResultSet → Statement → Connection,否则可能抛异常中断后续关闭
- 每个 close() 都要包在独立的 try-catch 中,避免前一个 close 失败导致后面资源无法释放
- 变量声明必须在 try 外(如 Connection conn = null),否则 finally 里访问不到
- 不要只关 Connection 就以为万事大吉——Statement 和 ResultSet 同样会泄漏,尤其 ResultSet 持有大量结果集数据缓存
检查 ORM 框架是否帮你兜底
用 MyBatis、Hibernate 等框架时,很多人误以为“框架自动管理”,其实不然:
立即学习“Java免费学习笔记(深入)”;
- MyBatis 的 SqlSession 必须显式 close() 或交由 Spring 管理生命周期;手写 SqlSession.open() 后不 close() 就等于裸连泄漏
- Hibernate 的 Session 同理,非 Spring 环境下需手动 close();即使用了 Spring,也要确认是否配置了正确的事务传播和 Session 绑定机制
- 警惕自定义 JDBC 工具类——如果封装了 getConnection() 却没配套提供 safeClose() 或统一回收逻辑,极易埋雷
加一层防护:启用连接池泄漏检测
光靠编码规范不够,运行时监控才是最后一道防线:
- HikariCP 设置 leakDetectionThreshold=30000(30秒),超时未归还会打印堆栈,精准定位哪行代码拿了连接不放
- Druid 设置 removeAbandonedOnBorrow=true 和 removeAbandonedTimeoutMillis=60000,可主动回收“疑似泄漏”的连接
- 配合监控看板观察 activeConnections、idleConnections、poolUsage,发现连接数缓慢爬升就是典型泄漏信号


















