尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

问题现场:线上内存飙高,OOM 报警

问题现场:线上内存飙高,OOM 报警
📅 发布时间:2026/6/26 3:04:25

,线上老项目突然收到服务器内存使用率持续飙高的报警,紧接着应用直接抛出 OOM 错误,服务崩溃。

紧急拉取了堆 Dump 文件,用 JProfiler 打开后,直接看到了内存占用的元凶:

  • 大量com.alibaba.druid.proxy.jdbc相关对象堆积
  • 堆中最大的单个对象是一个char[],大小超过 500MB,存储的正是项目中执行的 SQL 字符串

结合项目业务场景,初步判断是数据库操作相关的内存泄漏,定位方向直接锁定了代码中的 SQL 操作和 Druid 连接池配置。


二、根因定位:双重问题叠加导致的灾难

顺着堆 Dump 里的 SQL 文本,我直接定位到了业务代码,发现这次 OOM 是两个问题叠加导致的。

1. 业务代码:SQL 拼接逻辑导致大对象堆积

这是一个老项目,当年的开发同学已经离职了,代码里存在这样的逻辑:

  • 单条INSERT语句中,通过循环拼接 SQL 字符串,一次性插入大量数据
  • 当数据量较大时,拼接后的 SQL 字符串会变得非常大,生成的char[]对象直接占用几百 MB 内存
  • 这些大字符串被线程栈引用,短时间内无法被 GC 回收,直接推高了内存水位

2. 框架层面:Druid 1.1.22 版本的经典 SQL 缓存泄漏

堆 Dump 中大量的 Druid 对象,指向了一个更致命的问题:Druid 连接池的 SQL 统计缓存。

  • 项目使用的 Druid 版本是1.1.22,这个版本存在一个广为人知的问题:SQL 统计功能会无限制缓存所有执行过的 SQL 字符串,无法自动清理
  • 项目中拼接的大量不同 SQL,会被 Druid 全部缓存到sqlStatMap中,这些对象会一直持有 SQL 字符串的引用,导致它们无法被 GC 回收
  • 随着服务运行时间增长,缓存的 SQL 越来越多,内存只会涨不会跌,最终撑满堆内存,触发 OOM

三、解决方案:两步走彻底根治问题

针对这两个问题,我们采用了业务+框架双管齐下的修复方案,从根源解决内存泄漏。

第一步:优化 Druid 配置,掐断缓存泄漏

直接修改项目的 Druid 配置,关闭无限制的 SQL 统计,同时限制缓存大小,避免内存无限增长。

方案 A:彻底关闭 SQL 统计(推荐,零泄漏风险)
spring: datasource: druid: filter: stat: enabled: false # 关闭导致内存泄漏的SQL统计 web-stat-filter: enabled: false # 关闭Web统计,减少额外内存占用
方案 B:保留监控,限制缓存大小(折中方案)

如果业务必须保留 SQL 监控,可以通过配置限制缓存的 SQL 数量,避免无限增长:

spring: datasource: druid: filter: stat: enabled: true max-stat-count: 200 # 限制最多缓存200条SQL,超出自动淘汰

第二步:重构业务代码,替换 SQL 拼接为批量插入

修改原有的 SQL 拼接逻辑,改为标准的批量插入方式,既避免了超大 SQL 字符串的生成,也提升了数据库写入性能。

改造前(问题代码)
// 循环拼接SQL,生成超大字符串 StringBuilder sql = new StringBuilder("INSERT INTO t_invoice (col1, col2) VALUES "); for (Invoice invoice : list) { sql.append("(?, ?),"); } jdbcTemplate.update(sql.toString(), params);
改造后(批量插入)
// 使用JdbcTemplate批量插入,避免生成超大SQL字符串 String sql = "INSERT INTO t_invoice (col1, col2) VALUES (?, ?)"; jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { ps.setString(1, list.get(i).getCol1()); ps.setString(2, list.get(i).getCol2()); } @Override public int getBatchSize() { return list.size(); } });

四、效果验证与后续优化

改造完成后,我们重新上线服务并进行了压测验证:

  1. 内存曲线恢复平稳,不再出现持续飙高的情况
  2. 堆 Dump 中 Druid 相关对象和大char[]基本消失
  3. 数据库写入性能也有明显提升,单批次插入耗时降低了 40%

额外优化建议

  • 对于老项目,建议升级 Druid 到最新稳定版(如1.2.20+),修复了大量已知的内存泄漏问题
  • 批量插入时,建议设置合理的批次大小(如每批 100-500 条),避免单次操作过大导致数据库压力
  • 上线前务必进行压测,通过 JProfiler 或 Arthas 观察内存变化,提前发现潜在问题

五、踩坑总结

这次 OOM 排查给了我两个深刻的教训:

  1. 老项目的依赖版本一定要关注:Druid 1.1.22 这个版本的 SQL 缓存泄漏问题非常普遍,很多线上 OOM 都源于此,升级或关闭统计是最直接的解决方式。

相关新闻

  • AI 工程的四次进化,从「怎么写 Prompt」到「怎么造一套让 AI 不翻车的系统」
  • 大模型推理的“两步走”:Prefill 与 Decode 全流程科普详解
  • Windows与Office激活难题的终极解决方案:KMS_VL_ALL_AIO智能脚本指南

最新新闻

  • Security Onion:一体化开源安全监控平台部署与实战指南
  • 在Windows上进行Docker 部署速成指南(SpringBoot + Vue + MySQL + Redis)
  • Obsidian Excel转Markdown表格插件:3分钟解决表格粘贴难题
  • 自我介绍与未来展望
  • 33-静态源码入库与异步落库:为什么静态结构要先缓存再落仓
  • 2026 年广州网站开发公司前十,综合实力榜单出炉

日新闻

  • Qwen2.5-Turbo百万上下文实战指南:百炼平台长文本处理全解析
  • 怎么监控对标账号更新,2026年作者监控工作流,5款深度对比
  • EdgeRemover:专业级Windows Edge浏览器管理工具,彻底解决顽固软件卸载难题

周新闻

  • Visual C++运行库修复终极指南:5分钟快速解决Windows软件启动错误
  • 手把手教你构建统计局地区经济数据爬虫:从环境搭建到数据持久化全指南
  • 2026多Agent深度解析:用AI团队替代单一模型,四种架构实战落地

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号