事件概述

事件1:8月1号晚上18点50分某服务宕机,容器OOM,随后非常快速的,该服务的4台容器全都宕机。

由于未添加HeapDumpOnOutOfMemoryError参数,无dump文件,排查近期上线功能内容,无果,且xxjob也有分流到其他容器,排除定时任务的影响,数据库也没死锁,只能添加-XX:HeapDumpPath参数待下次问题复现

事件2:8月6号早上10点40分发生宕机,容器再次OOM,从时间上看也并没有什么规律。

再次灰度发布新容器、扩容的同时,登陆旧容器应用查看dump文件,发现没有dump文件。排查发现是因为添加了-XX:+ExitOnOutOfMemoryError参数,于是在容器OOM时应用立即重启,未留下dump文件。

吸取本次教训,这次在测试环境配置参数后做混沌实验,注入OOM,测试能否正常dump文件,确保参数无误,下次出现问题能留下dump文件排查

事件3:8月15号晚上19点左右发生宕机,容器再次OOM,这次终于抓到dump文件了,灰发扩容的同时开始排查问题

问题排查和定位

使用MAT加载dump文件(Eclipse Memory Analyzer)

左上角 File >> Open Heap Dump >> 打开文件加载dump文件

PS:如果打开提示报错Out Of Memory ,需要在MAT的安装目录下找到MemoryAnalyzer.ini文件,并修改-Xmx改到比你的dump文件稍大些(我这里dump文件9G,直接给我的MAT干OOM了 )

看Reports

点击Leak Suspects 查看有内存泄漏嫌疑的地方

看Problem Suspect 1(问题嫌疑,有嫌疑的地方1)

圈红圈的地方,org.postgresql.core.v3.QueryExecutorImpl.processResults,从这里可以看出端疑,从方法名称可以看出来是对返回结果的处理,基本可以猜测是某个查询方法返回的对象过大或者过多导致的OOMProblem Suspect 2也是指向这个方法

Keywords

  • java.lang.Object[]
  • org.postgresql.core.v3.QueryExecutorImpl.processResults(Lorg/postgresql/core/ResultHandler;I)V
  • QueryExecutorImpl.java:2252

看Details

接下来我们点击Details看看具体的堆栈信息

Accumulated Objects by Class in Dominator Tree(按类聚合数据)

往下拉,这里可以看到 Accumulated Objects by Class in Dominator Tree

可以看到Tuple这个类有128万个,总共占用了3335MB,接近3个G的内存

查官方的文档可以看到这个类是 Class representing a row in a ResultSet. 也就是表示查出来的一个结果行,查了100多万个结果出来,那基本可以确定是有异常的sql了,多半是没有传递查询条件之类的,我们接着看

看Thread Stack

和我们平时看报错的堆栈信息一样去看,这里已经可以定位到具体的查询方法了,大概长这样(代码经过脱敏处理,隐去实际业务对象名称等):

@Override
public List<XXObject> getByXxIds(List<String> xxIds) {
QueryParam queryParam = new QueryParam();
queryParam.setXxIds(xxIds);
List<XXObject> xxObjectPOList = xxObjectRepository.listLines(queryParam);
return this.translate(xxObjectPOList);
}

这里已经开始猜测xxIds为空,无传入参数导致全表查询出了几百万行数据

但是为了实锤,本着严谨的态度,我们接着看

Thread Overview and Stacks

回到能看到线程的Shortest Paths To the Accumulation Point,右键》》Java Basics 》》Thread Overview and Stacks

这里可以看到刚刚占用内存的processResults结果处理方法,不过这里我们要看的是sql和入参,所以应该关注PreparedStatement方法

这里我们层层打开,可以看到preparedQuery,也就是加载的sql,其中的key就是就是执行的sql

而paramValues也就是对应的入参了,可以看到是空的,那就基本实锤了

最后结合日志,查看该方法操作的时间和系统三次宕机的时间是否对得上,结果是都对上了

这个是历史功能,按理说以前也应该会有这样的问题,那么为什么不会呢

继续排查日志,发现诱因是前端调整导致用户可以对已经操作过的单据再次执行该操作,而这时候已经没有需要操作的数据了,因而导致了后续的问题

根因分析

根本原因还是底层通用逻辑没有做好参数校验,接口的健壮性没有做好

长期解决方案及改进措施

1. 对整个系统中类似的查询接口审查参数校验是否完整

2. 检查其他服务的dump配置是否添加

3. 制定好规范、代码审查标准

4. 熔断,虽然本次事件没产生雪崩效应,但是没做熔断也是事实

5. 完善监控告警

6. 也许有时间(不可能)应该AOP做默认分页,超出一定数量的数据查询,需要自己评估并传递参数

 

PS:遇到OOM问题第一时间灰发新容器,加内存

遇到问题第一时间想到的肯定是灰发新容器,加内存等等,但是我个人加的还是比较扣扣搜搜,没舍得一次加到位,4台机器加到6台,8G加到10G,但是实际用户一次操作,十几G的内存就出来了,连点几下,一下子全挂了,下次遇到问题还是需要直接加倍,先稳住生产环境(机器多又不花我钱 ),稳住了才有精力去排查问题

记一次OOM的更多相关文章

  1. 记一次OOM问题排查过程

    上周运维反馈线上程序出现了OOM,程序日志中的输出为 Exception in thread "http-nio-8080-exec-1027" java.lang.OutOfMe ...

  2. 记一次oom问题排查

    大家好,我是大彬~ 今天给大家分享最近出现的OOM问题. 上周五早上,测试同学反馈测试环境的子系统服务一直超时,请求没有响应. 收到这个问题之后,我有点纳闷,最近这个系统也没有改动代码逻辑,怎么会突然 ...

  3. 记一次log4j日志导致线上OOM问题案例

    最近一个服务突然出现 OutOfMemoryError,两台服务因为这个原因挂掉了,一直在full gc.还因为这个问题我们小组吃了一个线上故障.很是纳闷,一直运行的好好的,怎么突然就不行了呢... ...

  4. 记一个有趣的Java OOM!

    原文:https://my.oschina.net/u/1462914/blog/1630086 引言 熟悉Java的童鞋,应该对OOM比较熟悉.该类问题,一般都比较棘手.因为造成此类问题的原因有很多 ...

  5. 记一次Elasticsearch OOM的优化过程——基于segments force merge 和 store type 转为 niofs

    首选,说明笔者的机器环境(不结合环境谈解决方案都是耍流氓): cpu 32核,内存128G,非固态硬盘: RAID0 (4T * 6),单节点,数据量在700G到1800G,索引15亿~21亿.敖丙大 ...

  6. 记一次ArrayList产生的线上OOM问题

    前言:本以为(OutOfMemoryError)OOM问题会离我们很远,但在一次生产上线灰度的过程中就出现了Java.Lang.OutOfMemoryError:Java heap space异常,通 ...

  7. 记一次线上 OOM 和性能优化

    大家好,我是鸭血粉丝(大家会亲切的喊我 「阿粉」),是一位喜欢吃鸭血粉丝的程序员,回想起之前线上出现 OOM 的场景,毕竟当时是第一次遇到这么 紧脏 的大事,要好好记录下来. 1 事情回顾 在某次周五 ...

  8. BUGFIX 09 - 记一次Java中String的split正则表达式匹配 - 引发`OutOfMemoryError: Java heap space`的oom异常 排查及解决 -Java根据指定分隔符分割字符串,忽略在引号里面的分隔符

    问题简述 说白了,Java根据指定分隔符分割字符串,忽略在引号(单引号和双引号)里面的分隔符; oom压测的时候,正则匹配"(?=(?:[^\"]*\"[^\" ...

  9. 记一次线上OOM问题分析与解决

    一.问题情况 最近用户反映系统响应越来越慢,而且不是偶发性的慢.根据后台日志,可以看到系统已经有oom现象. 根据jdk自带的jconsole工具,可以监视到系统处于堵塞时期.cup占满,活动线程数持 ...

  10. 记一次 android 线上 oom 问题

    背景 公司的主打产品是一款跨平台的 App,我的部门负责为它提供底层的 sdk 用于数据传输,我负责的是 Adnroid 端的 sdk 开发. sdk 并不直接加载在 App 主进程,而是隔离在一个单 ...

随机推荐

  1. post 报头注入

    1.user-agent 注入: 使用情况:万能密码无法绕过安全验证,用户名无法注入,通过查看源代码分析执行的动作. bp抓包修改user_agent的数据如下 User-Agent: ' or ud ...

  2. git 切换分支和删除本地分支

    如果手误,创建了一个命名不规范的本地分支,以删除dev分支为例,可通过如下方法删除: 先切换到别的分支: git checkout otherBranch 删除本地分支: git branch -d ...

  3. 运维人员常用Linux命令汇总

    作为运维人员,这些常用命令不得不会,掌握这些命令,工作上会事半功倍,提供工作效率. 一.文件和目录 cd命令,用于切换当前目录,它的参数是要切换到的目录的路径,可以是绝对路径,也可以是相对路径. cd ...

  4. 「Log」做题记录 2024.1.1-2024.1.28

    \(2024.1.1-2024.1.7\) \(\color{blueviolet}{P1501}\) LCT 板子,链加链乘查询链和,断边加边. \(\color{black}{P4332}\) L ...

  5. php数据结构中的链表

    本文由 ChatMoney团队出品 链表的基本概念 链表(Linked List)是一种常见的数据结构,它由一系列节点组成,每个节点除了存储数据外,还包含指向下一个节点的指针.与数组相比,链表在插入和 ...

  6. python开发之路【第一章】:计算机基础结构

    1.1计算机基础 1.1.1 硬件 1944年,美籍匈牙利数学家冯·诺依曼提出计算机基本结构. 五大组成部分:运算器.控制器.存储器.输入设备.输出设备. -- 运算器:按照程序中的指令,对数据进行加 ...

  7. 鸿蒙Next仓颉语言开发实战教程:懒加载

    今天要分享的是仓颉开发语言中的懒加载. 先和初学者朋友们解释一下什么是懒加载.懒加载在代码中叫做LazyForEach,看到名字你一定能猜到它和ForEach的功能类似.只不过和ForEach的一次性 ...

  8. Java源码分析系列笔记-5.AQS

    目录 1. 是什么 2. 如何使用 3. 原理分析 3.1. 构造方法 3.1.1. 由头尾节点和代表锁状态的字段组成 3.1.2. Node是个双向队列节点 3.2. 获取锁的逻辑 3.2.1. 尝 ...

  9. MySQL核心知识学习之路(5)

    作为一个后端工程师,想必没有人没用过数据库,跟我一起复习一下MySQL吧,本文是我学习<MySQL实战45讲>的总结笔记的第五篇,总结了MySQL索引相关的实践使用问题. 上一篇:MySQ ...

  10. java--Spring框架核心

     * Struts与Hibernate可以做什么事? Struts, Mvc中控制层解决方案 可以进行请求数据自动封装.类型转换.文件上传.效验- Hibernate, 持久层的解决方案: 可以做到 ...