生产事故-记一次特殊的OOM排查
事故背景2023年3月10日14时19分C公司开发人员向A公司开发人员反映某开放接口从2023年3月10日14时许开始无法访问和使用。该系统为某基础数据接口服务基于 HTTP 协议进行通信。按照惯例首先排查网络是否异常经运维人员检查证明网络连通性没有问题。A公司开发组于2023年3月10日14时30分通知运维人员重启应用服务期间短暂恢复正常。但是很快十分钟后电话再次响起告知服务又出现异常无法访问。为了避免影响进一步扩大A公司决定将程序紧急回滚至上一稳定版本。回滚后系统业务功能恢复正常。短暂松一口气后开始排查问题。0x02 事故分析让运维拷贝和固定了更新前后的系统日志和应用包。根据前面的故障现象初步猜测是内存问题好在应用启停脚本中增加了参数-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/app/logs/app.dump对于无法在生产环境上使用jstack、jmap等命令直接查错的——事实上大多数时候都不能dump文件显得尤为重要果不其然日志目录下出现了app.dump文件在日志中搜索找到了若干处内存溢出错误java.lang.OutOfMemoryError: Java heap space但是令人费解的是每次出现OOM错误的位置居然都不一样事情逐渐变得复杂起来。用MAT(Memory Analyzer Tool)工具打开转储文件原以为会发现某个类型对象占用大量的内存结果出乎意料Histogram直方图中显示活跃对象居然只有100多M尝试 Calculate Precise Retained Size计算精确大小计算结果与前面相差不大。检查 Outgoing References 追踪引用对象和 Incoming References追踪被引用对象也未见明显异常令人头大。擦擦汗日志已经明确提示我们java.lang.OutOfMemoryError: Java heap space首先肯定这是一个堆内存空间引起的问题可能的原因有内存加载数据量过大例如不受行数限制的数据库查询语句或者不限制字节数的文件读取等事故系统显然没有这些情况内存泄漏资源未关闭/无法回收当系统存在大量未关闭的 IO 资源或者错误使用ThreadLocal等场景时也会发生OOM经排查也不存在这种情况系统内存不足系统内存不足以支撑当前业务场景所需要的内存过小的机器内存或者不合理的JVM内存参数。如果排除所有合理选项最不合理那个会不会就是答案呢遂开始检查机器的内存根据运维的说法机器内存为16GBtop命令查看java进程占用内存约为7.8GB看起来似乎没毛病。但是随后另一个同事注意到了一个事情最后一次系统升级的时候改动过应用启停脚本对比旧版本的脚本发现差异部分就是内存参数旧版本原为-Xms8g -Xmx8g -Xmn3g新版本改为-Xms8g -Xmx8g -Xmn8g看到这里屏幕前的一众同事都无语啊……0x03 事故原因为什么-Xmn参数设置成与-Xmx参数一样的大小会导致OOM呢该项目使用的JDK版本为1.8看看JDK 8的内存模型不难发现Heap Space Size Young Space Size Old Space Size而-Xmn参数控制的正是 Young 区的大小当堆区被 Young Gen 完全挤占又有对象想要升代到 Old Gen 时发现 Old 区空间不足于是触发 Full GC触发 Full GC 以后呢通常又会面临两种情况Young 区又刚好腾出来一点空间对象又不用放到 Old 区里面了皆大欢喜Young 区空间还是不够对象还是得放到 Old 区Old 区空间不够卒喜提OOM诶就是奔着 Old 区去的管你 Young 不 YoungOld 区空间不够卒喜提OOM这个就解释了为什么系统刚刚启动时会有一个短时间正常工作的现象随后当某段程序触发 Old Gen 升代时就会发生随机的OOM错误。那么什么时候对象会进入老年代呢这里也很有意思不妨结合日志里面出现OOM的地方对号入座经历足够多次数 GC 依然存活的对象申请一个大对象比如超过 Eden 区一半大小GC 后 Eden 区对象大小超过 S 区之和Eden 区 S0 区 GC 后S1 区放不下换言之正常情况下-Xmn参数总是应当小于-Xmx参数否则就会触发OOM错误。我们可以构造一个简单的例子来验证这个场景。首先是一个简单的SpringBoot程序package com.example.oom; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; SpringBootApplication public class OomApplication { static final byte[] ARRAY new byte[128 * 1024 * 1024]; public static void main(String[] args) { SpringApplication.run(OomApplication.class, args); } RestController public static class OomExampleController { GetMapping(/oom) public int oom() { byte[] temp new byte[128 * 1024 * 1024]; temp[0] (byte) 0xff; temp[temp.length - 1] (byte) 0xef; int noise new Random().nextInt(); ARRAY[0] (byte) (temp[0] temp[temp.length - 1] noise); return ARRAY[0]; } } }使用mvn clean package命令打包后我们用下面的命令启动它java -Xms512m -Xmx512m -Xmn512m -XX:HeapDumpOnOutOfMemoryError -XX:PrintGCDetails -XX:PrintGCDateStamps -XX:PrintHeapAtGC -Xloggc:gc.log -jar oom-1.0.0-RELEASE.jar然后借助Apache的ab.exe完成我们的验证测试。先是以1个并发访问100次上面的SpringBoot接口ab -c 1 -n 100 http://localhost:8080/oom你会发现它居然是可以正常运行的然后我们模拟用户负载上来之后的情况使用2个并发访问100次ab -c 2 -n 100 http://localhost:8080/oom如果前面的步骤都没错此时应该在SpringBoot应用控制台看到大量的OOM错误如下图所示然后在 GC 日志里面会看到触发 GC 的前后Old 区几乎都没有空间仅有的一点点还是JDK强行分配的在启动JVM时强制覆写了我们的-Xmn参数{Heap before GC invocations279 (full 139): PSYoungGen total 458752K, used 273877K [0x00000000e0080000, 0x0000000100000000, 0x0000000100000000) eden space 393728K, 69% used [0x00000000e0080000,0x00000000f0bf5798,0x00000000f8100000) from space 65024K, 0% used [0x00000000fc080000,0x00000000fc080000,0x0000000100000000) to space 65024K, 0% used [0x00000000f8100000,0x00000000f8100000,0x00000000fc080000) ParOldGen total 512K, used 506K [0x00000000e0000000, 0x00000000e0080000, 0x00000000e0080000) object space 512K, 98% used [0x00000000e0000000,0x00000000e007e910,0x00000000e0080000) Metaspace used 35959K, capacity 38240K, committed 38872K, reserved 1083392K class space used 4533K, capacity 4953K, committed 5080K, reserved 1048576K 2023-04-07T01:44:25.3480800: 57.446: [GC (Allocation Failure) --[PSYoungGen: 273877K-273877K(458752K)] 274384K-274384K(459264K), 0.0441401 secs] [Times: user0.06 sys0.30, real0.04 secs] Heap after GC invocations279 (full 139): PSYoungGen total 458752K, used 273877K [0x00000000e0080000, 0x0000000100000000, 0x0000000100000000) eden space 393728K, 69% used [0x00000000e0080000,0x00000000f0bf5798,0x00000000f8100000) from space 65024K, 0% used [0x00000000fc080000,0x00000000fc080000,0x0000000100000000) to space 65024K, 9% used [0x00000000f8100000,0x00000000f86e2070,0x00000000fc080000) ParOldGen total 512K, used 506K [0x00000000e0000000, 0x00000000e0080000, 0x00000000e0080000) object space 512K, 98% used [0x00000000e0000000,0x00000000e007e910,0x00000000e0080000) Metaspace used 35959K, capacity 38240K, committed 38872K, reserved 1083392K class space used 4533K, capacity 4953K, committed 5080K, reserved 1048576K } {Heap before GC invocations280 (full 140): PSYoungGen total 458752K, used 273877K [0x00000000e0080000, 0x0000000100000000, 0x0000000100000000) eden space 393728K, 69% used [0x00000000e0080000,0x00000000f0bf5798,0x00000000f8100000) from space 65024K, 0% used [0x00000000fc080000,0x00000000fc080000,0x0000000100000000) to space 65024K, 9% used [0x00000000f8100000,0x00000000f86e2070,0x00000000fc080000) ParOldGen total 512K, used 506K [0x00000000e0000000, 0x00000000e0080000, 0x00000000e0080000) object space 512K, 98% used [0x00000000e0000000,0x00000000e007e910,0x00000000e0080000) Metaspace used 35959K, capacity 38240K, committed 38872K, reserved 1083392K class space used 4533K, capacity 4953K, committed 5080K, reserved 1048576K 2023-04-07T01:44:25.3920800: 57.490: [Full GC (Ergonomics) [PSYoungGen: 273877K-142631K(458752K)] [ParOldGen: 506K-506K(512K)] 274384K-143137K(459264K), [Metaspace: 35959K-35959K(1083392K)], 0.0248171 secs] [Times: user0.14 sys0.00, real0.03 secs]接着无需改动任何代码我们调整下启动参数像这样

相关新闻

音频转会议纪要免费版够用吗?2026实测经验给出靠谱实用结论

音频转会议纪要免费版够用吗?2026实测经验给出靠谱实用结论

先说明白核心判断 作为长期测AI效率工具的博主,我2026年2月实测了5款主流工具的免费版,结论很明确:音频转会议纪要的免费版不是绝对够用或不够用,核心看你的使用场景——每月只用到1-2次、单场音频不超过1小时的轻度需求&#xf…

2026/7/2 5:53:53阅读更多 →
智能工牌合规方案:授权录音、加密传输与最小权限控制的工程实践

智能工牌合规方案:授权录音、加密传输与最小权限控制的工程实践

门店销售接待的过程,长期以来像一个“黑盒”——管理者知道结果,却难以看清每一次接待中到底发生了什么。销售人员的话术是否到位、客户的真实意向如何、哪些环节导致了丢单,往往只能凭感觉复盘。随着AI技术落地,一批专门针对销售…

2026/7/2 5:48:53阅读更多 →
3分钟上手TranslucentTB:让你的Windows任务栏焕然一新的透明美化神器

3分钟上手TranslucentTB:让你的Windows任务栏焕然一新的透明美化神器

3分钟上手TranslucentTB:让你的Windows任务栏焕然一新的透明美化神器 【免费下载链接】TranslucentTB A lightweight utility that makes the Windows taskbar translucent/transparent. 项目地址: https://gitcode.com/gh_mirrors/tr/TranslucentTB 想要摆脱…

2026/7/2 5:48:53阅读更多 →
【JetBrains认证工程师亲授】:IDEA SQL Console导出避坑清单(含12个致命配置陷阱)

【JetBrains认证工程师亲授】:IDEA SQL Console导出避坑清单(含12个致命配置陷阱)

更多请点击: https://codechina.net 第一章:SQL Console导出功能概述与核心价值 SQL Console导出功能是现代数据库管理平台中一项关键的生产力工具,它允许开发者、DBA及数据分析师在不离开浏览器界面的前提下,将查询结果以多种结…

2026/7/2 7:24:03阅读更多 →
DBeaver 数据编辑实战:批量更新 500+ 行记录的事务控制三步法

DBeaver 数据编辑实战:批量更新 500+ 行记录的事务控制三步法

1. 批量更新不是“点保存”那么简单:500行背后的事务悬崖 我第一次在生产环境用 DBeaver 直接编辑 623 行用户状态字段时,点了“Apply”之后盯着进度条看了 17 秒——第 489 行报错,整个事务回滚,但前 488 行的 UPDATE 语句已经发给了数据库。更糟的是,DBeaver 的“Undo”…

2026/7/2 7:24:03阅读更多 →
可扩展小说下载器:如何为100+网站构建智能离线阅读解决方案

可扩展小说下载器:如何为100+网站构建智能离线阅读解决方案

可扩展小说下载器:如何为100网站构建智能离线阅读解决方案 【免费下载链接】novel-downloader 一个可扩展的通用型小说下载器。 项目地址: https://gitcode.com/gh_mirrors/no/novel-downloader 在数字阅读时代,小说网站的反爬机制日益复杂&#…

2026/7/2 7:24:03阅读更多 →
DBeaver SQL编辑器语法高亮失效?3步修复+5种格式化快捷键实测

DBeaver SQL编辑器语法高亮失效?3步修复+5种格式化快捷键实测

1. 语法高亮失效不是Bug,是DBeaver在“忘记你是谁” 上周三下午,我接手一个遗留金融系统数据库迁移任务,打开DBeaver 24.1.0连上Oracle 12c,写第一条SELECT /*+ FULL(t) */ * FROM trade_log t WHERE ...时,发现注释块没变绿、关键字SELECT灰扑扑的、表别名t和字段名全无…

2026/7/2 7:24:03阅读更多 →
问题管理:为什么故障修了一次又一次,同样的问题还是反复出现?

问题管理:为什么故障修了一次又一次,同样的问题还是反复出现?

很多企业的 IT 服务台都会遇到一种很典型的情况:某个业务系统每隔一段时间就访问变慢,工程师每次都能通过重启服务、清理缓存、调整参数临时恢复;某个打印服务经常异常,处理人员每次都能重新连接或重装驱动;某类账号登…

2026/7/2 7:24:03阅读更多 →
MyBatis XML跳转成功率<67%?实测17款插件后,仅这1款通过IDEA官方插件市场严苛审核(含SHA-256校验码)

MyBatis XML跳转成功率<67%?实测17款插件后,仅这1款通过IDEA官方插件市场严苛审核(含SHA-256校验码)

更多请点击: https://intelliparadigm.com 第一章:MyBatis XML跳转失效的行业困局与技术归因 在大型 Java 企业级项目中,MyBatis 的 XML 映射文件(如 UserMapper.xml)与接口方法之间的 IDE 跳转功能频繁失效&#xf…

2026/7/2 7:19:02阅读更多 →
AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

6个月前的2025年12月,Boris Cherny 公开宣布自己卸载了 IDE。一时间,Vibe Coding 成了全行业最热的话题。6个月后,当我们回过头来拉一份真实账本,发现事情远没有"一句话生成一个App"那么浪漫。本文从产品经理和研发两个…

2026/7/1 4:42:14阅读更多 →
审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

引言:审计结束三个月了,审计员的权限还没关某城商行每年按照监管要求开展至少一次数据安全审计。审计期间,内审部门需要抽样检查各类业务数据——交易流水、客户信息、员工操作日志、权限配置记录。这些数据分布在不同系统中,审计…

2026/7/1 5:19:01阅读更多 →
塞尔达传说旷野之息存档修改器:3分钟掌握海拉鲁世界自由定制技巧

塞尔达传说旷野之息存档修改器:3分钟掌握海拉鲁世界自由定制技巧

塞尔达传说旷野之息存档修改器:3分钟掌握海拉鲁世界自由定制技巧 【免费下载链接】BOTW-Save-Editor-GUI A Work in Progress Save Editor for BOTW 项目地址: https://gitcode.com/gh_mirrors/bo/BOTW-Save-Editor-GUI 想在《塞尔达传说:旷野之息…

2026/7/2 0:03:01阅读更多 →
告别 AccessKey:多云平台 CLI OAuth 免密认证完全指南

告别 AccessKey:多云平台 CLI OAuth 免密认证完全指南

在本地开发环境使用云厂商 CLI 时,传统的 AccessKey(AK)方式需要手动创建、下载和保管密钥,不仅繁琐,还存在泄漏风险。其实,主流云平台都已提供基于 OAuth 2.0 的免密认证方案,让开发者可以通过浏览器登录一次性完成授权,CLI 自动管理临时凭证的刷新,兼顾了便利与安全…

2026/7/2 0:03:01阅读更多 →
基于13DOF传感器与PIC32MZ的高精度嵌入式导航系统设计

基于13DOF传感器与PIC32MZ的高精度嵌入式导航系统设计

1. 项目背景与核心价值在嵌入式系统开发领域,高精度定位与导航一直是极具挑战性的技术方向。传统方案往往面临成本、精度和实时性难以兼顾的困境。这个项目通过13DOF(13自由度)传感器组合与PIC32MZ2048EFH100高性能MCU的协同工作,…

2026/7/2 0:03:01阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/2 0:33:58阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/2 1:32:11阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/2 1:50:13阅读更多 →