数据库连接未关闭引发的内存泄漏需分层排查:先依堆内存(jstat)或RSS(ps)增长特征区分堆内/堆外泄漏类型,再通过HikariCP泄漏检测、SHOW PROCESSLIST、ss/lsof锁定源头,最后验证ResultSet/Statement释放逻辑及try-with-resources完整性。

数据库连接未关闭引发的内存泄漏,不能只盯着 Connection 对象本身——真正卡住内存的是它背后绑定的 Socket 缓冲区、ResultSet 数据、驱动元数据缓存等堆外与堆内资源。排查要分层推进,先判类型、再锁源头、最后验释放逻辑。
看内存增长特征,区分堆内还是堆外泄漏
堆内存(jstat -gc)持续上涨且 Full GC 后不回落 → 重点查 ResultSet、PreparedStatement 没 close;
RSS 内存(ps aux --sort=-rss)稳步爬升但堆很稳 → 堆外泄漏嫌疑大,盯 Socket、Netty DirectBuffer、MySQL 接收缓冲区。
用 HikariCP 泄漏检测精准定位业务代码
配置必须生效,不只是加参数:
• 设置 spring.datasource.hikari.leak-detection-threshold=30000(建议为业务最大耗时的 1.5 倍)
• 开启 spring.datasource.hikari.log-connection-statement=true,记录每次 getConnection/归还动作
• 日志中出现 “Connection leak detection triggered” 时,调用栈会直接打到哪一行获取了连接却没还
交叉验证连接状态,确认物理连接是否真堆积
别只信应用日志,从 OS 和 DB 双向印证:
• 执行 ss -tulpn | grep :3306,比对 ESTABLISHED 连接数是否远超 maxPoolSize
• MySQL 中运行 SHOW PROCESSLIST,筛选 State 为 Sleep 或 Reading from net、Time > 60s 的连接
• 查 /proc/
检查代码里最容易漏掉的三个释放点
很多泄漏不是 Connection 没关,而是下游资源被忽略:
• try-with-resources 必须包含 Connection、PreparedStatement、ResultSet 三者,缺一不可
• 避免手动 new PreparedStatement 或 DriverManager.getConnection() —— 绕过连接池等于放弃自动回收保障
• 复用 PreparedStatement 时,确认内部没强引用旧 ResultSet,否则 MySQL 8.0+ 驱动会缓存整块结果集
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











