生产环境JVM内存溢出案例分析-阿里云开发者社区

开发者社区> 丁威> 正文

生产环境JVM内存溢出案例分析

简介: 线上JVM内存溢出案例分析详解,本文详细的介绍了生产环境JVM内存溢出的故障分析过程,更是提供在故障发生之前如何快速隔离故障源、防止故障扩散等方法。
+关注继续查看

作者简介:《RocketMQ技术内幕》作者,维护“中间件兴趣圈”微信公众号,关注主流开源中间件。


如果我们所在公司的业务量比较大,在生产环境经常会出现JVM内存溢出的现象,那我们该如何快速响应,快速定位,快速恢复问题呢?

本文将通过一个线上环境JVM内存溢出的案例向大家介绍一下处理思路与分析方法。

案例:架构组接到某项目组反馈,Zabbix监控上显示JMX不可用,请求协助处理。

分析思路:

  • JMX不可用,往往是由于垃圾回收时间停顿时间过长、内存溢出等问题引起的。
  • 线上故障分析的原则是首先要采取措施快速恢复故障对业务的影响,然后才是采集信息、分析定位问题,并最终给出解决办法。

具体分析过程如下。

1、如何快速恢复业务

通常线上的故障会对业务造成重大影响,影响用户体验,故如果线上服务器出现故障,应规避对业务造成影响,但不能简单的重启服务器,因为需要尽可能保留现场,为后续的问题分析打下基础。

那我们如何快速规避对业务的影响,并能保留现场呢?

通常的做法是隔离故障服务器。

通常线上服务器是集群部署,一个好的分布式负载方案会自动剔除故障的机器,从而实现高可用架构,但如果未被剔除,则需要运维人员将故障服务器进行剔除,保留现场进行分析。

发生内存泄露,通常情况下是由于代码的原因造成的,一般无法立即对代码进行修复,很容易会发送连锁反应造成应用服务器一台一台接连宕机,故障面积会慢慢扩大,针对此种情况,应快速定位发生内存泄露的原因,将该服务进行降级,避免对其他服务造成影响。最简单的降级方法是根据F5(Nginx)转发策略,对该功能定向到一个单独的集群,与其他流量进行隔离,确保其他业务不受牵连,给故障排查、解决提供宝贵的缓冲时间。

2、分析解决问题

首先可以通过查看日志,确定是哪种内存溢出,堆内存溢出可发生的地方:Java heap space(堆空间)、perm space(持久代)。
c

2.1 收集内存溢出Dump文件

收集Dump文件有两种方式:

  • 设置JVM启动参数

    -XX:+HeapDumpOnOutOfMemoryError
    -XX:HeapDumpPath=/opt/jvmdump
    

在每次发生内存溢出时,JVM会自动将堆转储,dump文件存放在-XX:HeapDumpPath指定的路径下。

  • 使用jmap命令收集

    通过jmap -dump:live,format=b,file=/opt/jvm/dump.hprof pid。
    

2.2 分析Dump文件

在获取Dump文件后,可以使用工具MAT(MemoryAnalyzer)进行分析,该工具大家可以通过百度自行下载。

使用MAT打开Dump文件后,首页截图如下:
c

工具按钮介绍:

c1:直方图视图,将堆中所有的内存消耗情况统计出来,其如图所示:

c

c2:内存使用树状结构,以线程为维度,树状形式展开,如图所示:

c

c3线程栈,其截图如下:

c

c

根据该图,可以明确,堆的总大小为1.9G,被4个线程全部占据,导致其他线程无法再申请资源,抛出堆内存溢出错误。

接下来,我通常的做法是直接去看c2这个视图(以线程为基本维度,查找线程中占用内存的对象),为后续定位排查提供必要的依据。

c
c

从上面的截图中可以得出如下关键信息点:

  • org.apache.ibatis.executor.result.DefaultResultHandler内部持有一个List,其原始为java.util.HashMap,从这个类基本可以看出是与数据库的查询相关,对数据库返回结果的解码并组织成HashMap。
  • 这个List中的元素总共有146033个,初步可以判断出是在一次查询中从数据库中一次查询出了太多数据,造成了内存溢出。

由于SQL查询代码中,是用HashMap来接收数据库中的返回字段,无法一时间看出是那个查询,那我们能不能精确找到是哪一个查询,哪一行代码,甚至与哪一条SQL语句呢?

答案是可以的,我们可以从c3视图一探究竟。

温馨提示:
c2视图使用技巧:展开技巧:沿着使用率最高的项一层一层进行展开,直至发现具体占用内存的对象。

接下来我们从c3视图去寻找是哪个方法,哪条SQL语句触发的。

具体方法:首先完全展开一个线程,从展开图的底部向上寻找:
其线程的入口(控制层代码)

c

继续往上查找,要找到SQL语句,应该找到Mybatis处理结果集相关的类,如图所示:

c

然后展开boundSql即能找到SQL语句:

c

然后鼠标可以放在SQL属性中,右键,可以将SQL语句复制出来。
c

由于这里涉及到公司的代码机密,故在这里不贴出具体的SQL语句。

这里根据后面的分析,原来是在做导出功能的时候,没有使用分页对数据进行分页查询,分页写入Excel文件,而是一次将全部数据查询,导致导出功能如果并发数超过4个时,就会将所有内存耗尽。

解决方案:

  • 首先在运维层面将该请求导入到指定的一台服务器上,是导出任务与其他任务进行隔离,避免对其他重要服务造成影响。
  • 项目组对其代码进行修复,可以使用分页查数据,然后分配写入Excel。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
面试官,Java8 JVM内存结构变了,永久代到元空间
面试官,Java8 JVM内存结构变了,永久代到元空间
14 0
从源码分析如何优雅的使用 Kafka 生产者
前言 在上文 设计一个百万级的消息推送系统 中提到消息流转采用的是 Kafka 作为中间件。 其中有朋友咨询在大量消息的情况下 Kakfa 是如何保证消息的高效及一致性呢? 正好以这个问题结合 Kakfa 的源码讨论下如何正确、高效的发送消息。
543 0
sql server 2005日志文件过大问题解决后分析--针对发布订阅产生的日志问题
机房在四月份改造中对数据库的软硬件进行了升级,具体情况是:原先旧有的数据库是采用主备两台1u的单机,在windows 2000的系统下分别安装好sql server 2000后,在主数据库上做一天一次的完整备份和每隔两小时的差异备份,在完整备份的同时进行发布;备数据库则对主数据库的完整备份进行订阅。
920 0
只需4个步骤,分析解决在生产环境下JVM内存泄露问题
只需4个步骤,分析解决在生产环境下JVM内存泄露问题
4801 0
Java内存溢出OutOfMemoryError的产生与排查
在java的虚拟机异常中,有两个异常是大家比较关心的,一个是StackOverflowError,另一个是OutOfMemoryError。今天我们就来看看OutOfMemoryError是怎么产生的,以及如何去排查这个异常。
338 0
JVM源码分析之System.currentTimeMillis及nanoTime原理详解
##概述 上周`@望陶`问了我一个现象很诡异的问题,说JDK7和JDK8下的`System.nanoTime()`输出完全不一样,而且差距还非常大,是不是两个版本里的实现不一样,之前我也没注意过这个细节,觉得非常奇怪,于是自己也在本地mac机器上马上测试了一下,得到如下输出: ``` ~/
6571 0
没有监控过JVM内存的职场生涯,是不完美的
没有监控过JVM内存的职场生涯,是不完美的
7 0
+关注
丁威
RocketMQ早期布道师、RocketMQ社区视频直播讲师。《RocketMQ架构设计与实战原理》作者。
61
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载