-Xms8g -Xmx8g -Xmn3g 生产事故-记一次特殊的OOM排查

news2024/10/6 8:40:31

-Xms8g -Xmx8g -Xmn3g 生产事故-记一次特殊的OOM排查

事故背景

  • C公司开发人员向A公司开发人员反映某开放接口从2023年3月10日14时许开始无法访问和使用。该系统为某基础数据接口服务,基于 HTTP 协议进行通信。按照惯例,首先排查网络是否异常,经运维人员检查,证明网络连通性没有问题。A公司开发组于2023年3月10日14时30分通知运维人员重启应用服务,期间短暂恢复正常。但是,很快,十分钟后,电话再次响起,告知服务又出现异常,无法访问。为了避免影响进一步扩大,A公司决定将程序紧急回滚至上一稳定版本。回滚后,系统业务功能恢复正常。短暂松一口气后,开始排查问题。

事故分析

  • 让运维拷贝和固定了更新前后的系统日志和应用包。根据前面的故障现象,初步猜测是内存问题,好在应用启停脚本中增加了参数-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/logs/app.dump(对于无法在生产环境上使用jstackjmap等命令直接查错的——事实上大多数时候都不能,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内存参数。

  • 如果排除所有合理选项,最不合理那个会不会就是答案呢?遂开始检查机器的内存,根据运维的说法,机器内存为16GB,top命令查看java进程占用内存约为7.8GB,看起来似乎没毛病。

  • 但是随后另一个同事注意到了一个事情,最后一次系统升级的时候,改动过应用启停脚本,对比旧版本的脚本,发现差异部分就是内存参数:

  • 旧版本原为:

  • -Xms8g -Xmx8g -Xmn3g
    
  • 新版本改为:

-Xms8g -Xmx8g -Xmn8g


-  看到这里,屏幕前的一众同事都无语啊…… 

## 事故原因

-  为什么`-Xmn`参数设置成与`-Xmx`参数一样的大小会导致`OOM`呢?该项目使用的*JDK*版本为1.8,看看*JDK 8*的内存模型: 
-  ![JDK8内存模型](https://img-blog.csdnimg.cn/img_convert/eb4d3fd8c58a30fb83a104fe45925802.png) 

-  不难发现,`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 不 Young,Old 区空间不够,卒,喜提`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
    
  • 然后借助Apacheab.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错误,如下图所示:

  • 模拟OOM结果

  • 然后在 GC 日志里面会看到,触发 GC 的前后,Old 区几乎都没有空间,仅有的一点点还是JDK强行分配的(在启动JVM时强制覆写了我们的-Xmn参数):

  • {Heap before GC invocations=279 (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.348+0800: 57.446: [GC (Allocation Failure) --[PSYoungGen: 273877K->273877K(458752K)] 274384K->274384K(459264K), 0.0441401 secs] [Times: user=0.06 sys=0.30, real=0.04 secs] 
    Heap after GC invocations=279 (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 invocations=280 (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.392+0800: 57.490: [Full GC (Ergonomics) [PSYoungGen: 273877K->142631K(458752K)] [ParOldGen: 506K->506K(512K)] 274384K->143137K(459264K), [Metaspace: 35959K->35959K(1083392K)], 0.0248171 secs] [Times: user=0.14 sys=0.00, real=0.03 secs] 
    
  • 接着无需改动任何代码,我们调整下启动参数,像这样:

  • java -Xms512m -Xmx512m -Xmn64m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar oom-1.0.0-RELEASE.jar
    
  • 你会发现它又可以了。这是一个为了验证而打造的极端例子,实际上生产的应用情况会比这个复杂得多,但这并不妨碍我们理解它的意图。

事故复盘

  • 这是一场典型的”人祸“,来源于某个同事的”调优“,比起追究责任,更重要的是带给我们的启发:
    • 即使是应用启停脚本,也应该作为程序的一部分,纳入测试验证流程和上线检查清单,禁止随意变更;
    • 很多时候,默认的就是最好的,矫枉则常常过正。

事故影响

  • 造成C公司关键业务停摆半小时,生产系统紧急回滚一次。A公司相关负责人连夜编写事故报告一份。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1182877.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

5-Servlet

5-Servlet 文章目录 5-ServletServlet概述补充概念:静态资源和动态资源Servlet简介 Servlet开发流程开发方式1--XML配置步骤1:新建一个module,并将其改为Web类型步骤2:开发一个Java类,名为:UserServlet步骤…

【Spring】使用注解开发前提条件

前提&#xff1a;导入约束&#xff0c;添加注解支持 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xmlns:con…

浮点数存储

//浮点数存储 int main() {int n 9;//00000000 00000000 00000000 00001001//0 00000000 00000000000000000001001//S E(8bit) M(23bit)//E-126//M0.000000000000000000001001// 0.000000000000000000001001 *2^-126float* pFloat (float*)&n;printf("n的值…

阿里云中的云服务器的ubuntu中的vim没有显示行号

没有行号&#xff1a; 在终端输入命令&#xff1a; vim ~/.vimrc set nu

CRM客户管理系统究竟是什么?如何实施?

很多销售人员都不是特别喜欢使用信息化软件&#xff0c;然而从销售经理的角度看&#xff0c;信息化又的确提升了团队的管理效率和业绩。追究这些矛盾的原因&#xff0c;无外乎几点&#xff1a; 认知角度 → 销售员&#xff1a;数据没用又浪费我时间 VS 销售经理&#xff1a;数…

汉化程序的使用方法

首先&#xff1a;双击打开安装程序&#xff0c;输入密码

零代码编程:用ChatGPT批量提取flash动画swf文件中的mp3

文件夹&#xff1a;C:\迅雷下载\有声绘本_flash[淘宝-珍奥下载]\有声绘本 flash&#xff0c;里面有多个flash文件&#xff0c;怎么转换成mp3文件呢? 可以使用swfextract工具从Flash动画中提取音频&#xff0c;下载地址是http://www.swftools.org/download.html&#xff0c;也…

在HTML单页面中,使用Bootstrap框架的多选框如何提交数据

1.引入Bootstrap CSS和JavaScript文件&#xff1a;确保在HTML页面的标签内引入Bootstrap的CSS和JavaScript文件。可以使用CDN链接或者下载本地文件。 <link rel"stylesheet" href"https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css&q…

数据结构-双向链表

1.带头双向循环链表&#xff1a; 前面我们已经知道了链表的结构有8种&#xff0c;我们主要学习下面两种&#xff1a; 前面我们已经学习了无头单向非循环链表&#xff0c;今天我们来学习带头双向循环链表&#xff1a; 带头双向循环链表&#xff1a;结构最复杂&#xff0c;一般用…

UICollectionView左上对齐布局

最近完成的项目需要左上对齐的瀑布流&#xff0c;每个格子的尺寸不同&#xff0c;可以使用UICollectionView定义不同的尺寸&#xff0c;但是CollectionView的格子高度是相同的&#xff0c;我想要的是这样 左上对齐分别是0、1、2&#xff1b;3、4&#xff1b; 当前只能自定义一个…

音乐免费下载mp3格式+音频格式转换+剪辑音频+合并音频教程

1.在qq音乐网页版搜索想要的歌曲 qq音乐网站&#xff1a;https://y.qq.com/ 如果你是vip可以直接下载vip的歌曲&#xff0c;如果不是选择不是vip的歌曲进行第一步的操作 2.点击播放进入页面后F12拿到音频地址 然后双击src里面的音频地址复制 网页新标签打开赋值的这个链接&a…

Harbor私有仓库

Harbor私有仓库 文章目录 Harbor私有仓库Harbor简介&#xff1a;Harbor 提供了以下主要功能和特性&#xff1a;优缺点&#xff1a;环境说明&#xff1a;部署harbor1.永久关闭防火墙和seliux&#xff0c;配置阿里云源&#xff0c;添加映射关系2.安装docker&#xff0c;开启docke…

西瓜书笔记

周志华老师亲讲-西瓜书全网最详尽讲解-1080p高清原版《机器学习初步》 周志华机器学习&#xff08;西瓜书&#xff09;学习笔记&#xff08;持续更新&#xff09; 周志华《Machine Learning》学习笔记 绪论 基本术语 数据集&#xff08;data set&#xff09;&#xff1a;一堆…

常见React Hooks 钩子函数用法

一、useState useState()用于为函数组件引入状态&#xff08;state&#xff09;。纯函数不能有状态&#xff0c;所以把状态放在钩子里面。 import React, { useState } from react import ./Button.cssexport function UseStateWithoutFunc() {const [name, setName] useStat…

RK3588平台开发系列讲解(显示篇)MIPI 屏幕驱动调试

🚀返回专栏总目录 文章目录 一、背光驱动1.1、背光 PWM 节点设置1.2、backlight 节点设置二、屏幕初始化序列发送时序参数设置2.1、设备树下 DSI 节点编写2.2、DSI 的 panel 子节点编写沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 调试 MIPI 屏幕主要有三部分内容…

Qt Creator插件

这里以Qt Creator 4.15.2版本的源码为示例进行分析 源码结构如下&#xff0c;为了追溯其插件加载过程&#xff0c;从main.cpp入手 Qt Creator的插件目录&#xff0c;生成的插件&#xff0c;好几十个呢 Qt Creator插件的读取 int main(int argc, char **argv)中以下代码创建插…

jenkins Java heap space

jenkins Java heap space&#xff0c;是内存不够。 两个解决方案&#xff1a; 一&#xff0c;修改配置文件 windows系统中&#xff0c;找到Jenkins的安装路径&#xff0c; 修改jenkins.xml 将 -Xmx256m 改为 -Xmx1024m 或者更大 重启jenkins服务。 二&#xff0c;jenkins增…

海思SD3403/SS928开发板 开发记录二: 设置网络 telnet连接开发板

1.设置网络 设置桥接网络 并修改虚拟机IP网段 问题1.参照前一篇博客 2.ping 测试 主机 虚拟机 板端 相互通信 3.telnet 登录板端

Ps:自由变换

自由变换 Free Transform是 Photoshop 中最常用的命令之一&#xff0c;可对图层、图层蒙版、选区、选区内容等进行缩放、旋转、斜切、扭曲、透视等各种变换操作。 Ps菜单&#xff1a;编辑/自由变换 Edit/Free Transform 快捷键&#xff1a;Ctrl T 或者&#xff0c;在图层上右键…