事件概述

事件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. Git 查看 tag 标签详解

    摘要:介绍git中tag标签的使用方法,包括创建标签.提交标签.查询标签和删除标签等. 我们拿到一个即将投产的标签后,需要确认标签是否打在了正确的分支,故需要查看标签的详情信息,保证顺利上线.基于此背 ...

  2. Ingress学习笔记

    Ingress 我们已经知道,Service对集群之外暴露服务的主要方式有两种:NodePort和LoadBalancer,但是这两种方式,都有一定的缺点: NodePort方式的缺点是会占用很多集群 ...

  3. 学 Java 还是 Go 语言?这事儿很简单!

    相信很多学编程的同学都在纠结这个问题:学 Java 还是 Go 语言? 先给出省流结论,简单粗暴,就 4 个字:选 Java! 好,本期结束! 网上的讨论五花八门,有人说 Java 过时了,Go 才是 ...

  4. ESP32-Arduino物联网工控(一)串口转TCP转发机项目简介

    Arduino 在物联网上配合ESP32简直就是神器! 然后现在大老板说新设备用ESP32来帮助原本没有联网功能的STM32单片机来进行调试...... 实现需求: 1.指定命令拍照,协议HTTP,并 ...

  5. 使用acme.sh颁发TLS证书并安装到nginx/apache实现网站https访问

    原文永久链接:https://forum.piwind.com/d/22-shi-yong-acmeshban-fa-tlszheng-shu-bing-an-zhuang-dao-nginxapac ...

  6. MySQL 02 日志系统:一条SQL更新语句是如何执行的?

    比如执行一条更新语句: update T set c=c+1 where ID=2; 首先,更新语句也会走一遍查询语句的流程.除此以外,更新还涉及两个日志模块,分别是redo log和binlog. ...

  7. C# WinForm 多线程启动提示 DragDrop 注册失败

    https://blog.csdn.net/educast/article/details/7954242 这种情况是由多线程引起的,在项目中遇到过这样的情况,查了一下网上的解决方法...汗,都不行. ...

  8. from gi.repository import Gtk, GObject

    Traceback (most recent call last): File "/usr/bin/software-center", line 25, in from gi.re ...

  9. docker 开启远程访问功能

    简介 部署了一个http服务在docker上,由于docker有自己的端口似乎无法访问 参考链接 https://blog.csdn.net/longzhanpeng/article/details/ ...

  10. nii.gz 简介

    简介 这是一种医学设备生成的文件.实际上是一个三维矩阵. 用matlab的niftiread函数可以进行读取. Tips 对于大型文件的读取不知道 matlab做了什么优化比python的nibabe ...