记一次OOM导致服务器假死的排查过程

记一次OOM导致服务器假死的排查过程

  • 背景及现象
  • 问题发现
  • 获取Heap Dump
    • 1、使用Jmap命令导出
    • 2、使用 JVM 参数获取 dump 文件
  • 分析Heap Dump文件
    • MAT 安装
    • 使用MAT分析
  • 解决方案
  • Memmeory Analyzer Tool(MAT)简介
    • MAT介绍
      • 基础概念
      • Overview(概览)
      • Histogram(直方图)
      • Dominator Tree(支配树)
      • Leak Suspects Report(泄漏疑点报告)

背景及现象

2019-09-19 2019-09-20这两天小程序后台服务多次出现假死情况,服务没挂但是访问接口无响应。

问题发现

运维的监控脚本发现某服务器上的应用无法访问发送邮件给项目成员。
查看应用日志只发现了一条连接的错误信息如下:在这里插入图片描述
内容过于单薄,并不能通过该条日志定位到问题。
使用jmap -heap [pid] 查看当前应用的堆内存使用情况如下:
记一次OOM导致服务器假死的排查过程_第1张图片
重点关注Heap Usage,可见新生代和老年代的空间满了,这里可以确定问题是OutOfMemery(OOM)。
现在的问题转移到定位产生内存溢出的根源,这里要先获取heap dump信息。

获取Heap Dump

这里主要介绍两种:

1、使用Jmap命令导出

jmap -dump:live,format=b,file=heap.hprof 

2、使用 JVM 参数获取 dump 文件

  1. -XX:+HeapDumpOnOutOfMemoryError
    这个参数非常有用,需要我们分析内存的场景一般都是出现了OOM的情况。

  2. -XX:+HeapDumpBeforeFullGC
    当 JVM 执行 FullGC 前执行 dump。

  3. -XX:+HeapDumpAfterFullGC
    当 JVM 执行 FullGC 后执行 dump。

以上参数设置以后堆转储将默认在JVM的“当前目录”中生成,可结合如下命令显示重定向 dump 文件存储路径。
-XX:HeapDumpPath=test.hprof

注意:JVM 生成 Heap Dump 的时候,虚拟机是暂停一切服务的。如果是线上系统执行 Heap Dump 时需要注意。

得到dump文件后,将文件下载到本地备用。

分析Heap Dump文件

堆内存文件分析工具有几种,如jdk自带的Viusal VM,MAT,Jhat等。本次使用MAT来分析dump文件。MAT的内容参考[Memmeory Analyzer Tool(MAT)简介]

MAT 安装

下载地址:https://www.eclipse.org/mat/downloads.php
选择合适版本下载以后,解压到指定目录,右键执行MemoryAnalyzer.exe即可
记一次OOM导致服务器假死的排查过程_第2张图片

使用MAT分析

运行MAT,打开下载下来的dump文件(hprof类型)。如果发现文件加载缓慢或者有卡顿可以修改MemoryAnalyzer.ini配置,加大启动内存。其默认内存是512m,如果文件偏大建议加大启动内存。mat分析dump文件时比较耗内存,内存较小分析的速度就会偏慢甚至卡顿。
记一次OOM导致服务器假死的排查过程_第3张图片
默认选择Leak Suspects Report,自动分析生成内存泄漏疑点报告
记一次OOM导致服务器假死的排查过程_第4张图片
打开结果
记一次OOM导致服务器假死的排查过程_第5张图片
查看leak suspects报告
记一次OOM导致服务器假死的排查过程_第6张图片
记一次OOM导致服务器假死的排查过程_第7张图片
从报告上来看有1353759个ByteArrayRow对象。找一个ByteArrayRow对象查看详情
记一次OOM导致服务器假死的排查过程_第8张图片
很明显这是一个数据库记录,点击 list object ->with outgoing reference 查询该对象引用了哪些对象。
记一次OOM导致服务器假死的排查过程_第9张图片
记一次OOM导致服务器假死的排查过程_第10张图片
引用的对象有五个,应该这是一条数据库记录,所以直接看DruidPooledConnection对象
记一次OOM导致服务器假死的排查过程_第11张图片
复制SQL语句
记一次OOM导致服务器假死的排查过程_第12张图片
注意到Sql语句为:

    SELECT id, full_name, nick_name, union_id, avatar_url, gender, age, occupation, QQ, WW, month_income, MSN, card, photo_id,
    country, province, city, area, area_id, town, phone, telephone, mobilephone, email, birthday,
    register_time, channel, lastLoginDate, availableBalance, freezeBlance, gold, member_level_id, empirical_value,
    current_add_empirical_value, current_add_empirical_value_date, account_status, pay_time,
    pay_valid_time, update_time, register_mobile, is_delete
 
    FROM
    yh_full_member_0
    WHERE
    union_id=?
    AND
    is_delete=0

另外四个conn对象的sql语句与上述相同,唯一的差异就是表名为yh_full_member_1,yh_full_member_2,yh_full_member_3,yh_full_member_4。该SQL语句只传了一个参数union_id去查看参数值。选择stmt右键go into
记一次OOM导致服务器假死的排查过程_第13张图片
选择raw 右键go into
记一次OOM导致服务器假死的排查过程_第14张图片
选择parameterValues 右键Go Into 发现参数值为’’,空字符串
记一次OOM导致服务器假死的排查过程_第15张图片
记一次OOM导致服务器假死的排查过程_第16张图片
至此,问题已经定位到了,根据union_id查询数据时,union_id传值为空字符串,导致该条查询语句查询全表数据而创建了100多W个对象导致内存溢出,。有五个conn的原因是该项目的会员表使用Sharding sphere进行分表,yh_full_member共有五张表,平均每张表的记录大概是400多W。查询的语句为 select … from yh_full_member where … 经过Sharding根据路由规则实际改写成了五条sql语句,select … from yh_full_member_(0-4) …

解决方案

问题根源定位到了,那么解决方案自然而然就产生了。查询的时候,参数控制更加严格,即不允许空字符串。

Memmeory Analyzer Tool(MAT)简介

MAT是Memory Analyzer的简称,它是一款功能强大的Java堆内存分析器。可以用于查找内存泄露以及查看内存消耗情况。MAT是基于Eclipse开发的,是一款免费的性能分析工具。

MAT介绍

基础概念

  1. Shallow Heap 和 Retained Heap
    1) Shallow Heap表示对象本身占用内存的大小,不包含对其他对象的引用,也就是对象头加成员变量(不是成员变量的值)的总和。
    2)Retained Heap是该对象自己的Shallow Heap,并加上从该对象能直接或间接访问到对象的Shallow Heap之和。换句话说,Retained Heap是该对象GC之后所能回收到内存的总和。
    Retained Heap不包含除自身之外GC Root能直接访问的对象。比如GC Root 能访问A、C对象,A能直接或间接访问C对象,那么C对象是不计入在A对象的Retained Heap里的。
  2. 对象引用(Reference)
    对象引用按从最强到最弱有如下级别,不同的引用(可到达性)级别反映了对象的生命周期:
    强引用(Strong Ref):通常我们编写的代码都是强引用,于此相对应的是强可达性,只有去掉强可达性,对象才能被回收。
    软引用(Soft Ref):对应软可达性,只要有足够的内存就一直保持对象,直到发现内存不足且没有强引用的时候才回收对象。
    弱引用(Weak Ref):比软引用更弱,当发现不存在强引用的时候会立即回收此类型的对象,而不需要等到内存不足。通过java.lang.ref.WeakReference和java.util.WeakHashMap类实现。
    虚引用(Phantom Ref):根本不会在内存中保持该类型的对象,只能使用虚引用本身,一般用于在进入finalize()方法后进行特殊的清理过程,通过java.lang.ref.PhantomReference实现。
  3. GC Roots和Reference Chain
    JVM在进行GC的时候是通过使用可达性来判断对象是否存活,通过GC Roots(GC根节点)的对象作为起始点,从这些节点开始进行向下搜索,搜索所走过的路径成为Reference Chain(引用链),当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。

Overview(概览)

获取堆转储的概览信息:顶部是对象的大小和总数,然后是根据retained size排序的对象饼图。在导入堆转储的时候会自动打开Overview界面,或者点击工具栏上的I图标打开。
记一次OOM导致服务器假死的排查过程_第17张图片

Histogram(直方图)

直方图可通过工具栏上的直方图图标打开,或者在Overview页面Action标签下的Histogram。
直方图列出了按其类别分组的对象。内存分析器可以非常快速地估计保留的大小。这是继续进行分析的一个很好的指标。
Shallow Heao和Retained Heap参考Shallow Heap 和 Retained Heap默认的大小单位是 Bytes,可以在 Window - Preferences 菜单中设置单位。
记一次OOM导致服务器假死的排查过程_第18张图片
通过直方图视图可以很容易找到占用内存最多的几个类(通过Retained Heap排序),还可以通过其他方式进行分组(见下图)。记一次OOM导致服务器假死的排查过程_第19张图片

Dominator Tree(支配树)

可在工具栏点击Dominator Tree图标打开,或者Overview Action标签下的Dominator tree打开视图。
在此视图中列出了每个对象(Object Instance)与其引用关系的树状结构,同时包含了占用内存的大小和百分比。
通过Dominator tree视图可以很轻易得找到占用内存最大的几个对象(根据Retained Heap或Percentage排序),和Histogram类似,可以通过不同的方式进行分组显示。
记一次OOM导致服务器假死的排查过程_第20张图片

Leak Suspects Report(泄漏疑点报告)

打开方式:导入dump文件时,默认选择Leak Suspects Report或者在Overview页面下的Report标签中点击Leak Suspects
包括泄漏疑点和系统概述,如下图:
记一次OOM导致服务器假死的排查过程_第21张图片

你可能感兴趣的:(java)