• Java应用无响应、内存溢出、内存泄漏排查


    一、概述

    问题:客户反馈应用异常、访问服务出现504问题。

    现象:Nginx请求个别节点出现499错误码,应用节点无日志输出。

    二、分析思路

    1.排查进程占用内存

    • 使用ps命令查看内存占用情况。

    2.分析内存使用情况

    • 使用jstat工具查看Full GC情况,分析full gc次数是否频繁,确认应用本身是否有问题。

    • 使用jmap查看当前应用进程使用内存,分析是否存在内存飙升等问题。

    3.排查线程阻塞

    • 使用netstat检查应用的连接数,排除线程阻塞原因。

    三、实际操作

    1.排查进程占用内存

    方法一:

    # 第五列为虚拟内存占用情况,第六列为实际内存占用情况,默认单位kb。
    ps -aux | grep MyApp.jar
    # 实际内存以M为单位
    ps -aux | grep MyApp.jar | awk '{sum=$6/1024} {print $0 " " $1 " " sum " MB"}'
    
    • 1
    • 2
    • 3
    • 4

    方法二:

    # 通过ps查找进程id
    ps -ef | grep MyApp.jar
    # 通过top -p命令查看内存占用
    top -p <pid>
    
    • 1
    • 2
    • 3
    • 4

    2.分析内存使用情况

    Java Heap Dump 是特定时刻 JVM 内存中所有对象的快照。它们对于解决内存泄漏问题和分析 Java 应用程序中的内存使用情况非常有用。

    Java Heap Dump 通常以二进制格式的 hprof 文件存储。我们可以使用 jhat 或 JVisualVM 之类的工具打开和分析这些文件。同样,使用 MAT 工具分析是很常见的。

    1.生成dump日志
    • 启动参数配置生成dump日志:

      java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/tmp/heapdump.hprof -jar MyApp.jar
      
      • 1
    • 临时生成dump日志:

      如果没有配置dump日志,则不要直接停掉应用,先生成dump日志。

      # 0.查看PID
      ps -aux | grep MyApp.jar
      jps
      ###################################
      # 1.1生成dump日志
      jmap -dump:format=b,file=/tmp/jmap_heapdump.hprof <pid>
      # 1.2生成dump日志(live只保留存活的对象),如果运行时间长,防止heap过大
      jmap -dump:live,format=b,file=/tmp/heapdump.hprof <pid>
      ###################################
      # jcmd是java 8引入的集大成的诊断工具。
      # 使用jcmd生成dump日志
      jcmd <pid> GC.heap_dump /tmp/jcmd_heapdump.hprof
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    2.解析dump日志
    • JProfiler

    • visualvm

      官网:https://visualvm.github.io/

      github:https://github.com/oracle/visualvm

    3.生成GC日志
    • 启动参数配置生成gc日志:

      java -Xloggc:gc.log -jar MyApp.jar
      
      • 1
    4.定位具体代码
    # 1.找到cpu最高的进程的id
    top -c
    # 查询java进程前五并进行排序
    ps aux|grep java|grep -v grep|head -5|awk '{print $3,$1,$2}'|sort -rn
    # 2.打印这个进程的所有线程的运行堆栈
    jstack -l <pid> > /tmp/jstack.log
    # 3.当前进程所有线程消耗情况
    top -H -p <pid>
    ps -mp <pid> -o THREAD,tid,time
    # 4.找到CPU负载最高的线程, 把线程ID转换成16进制,(10进制转16进制,printf "%x\n" tid)
    printf "%x\n" <tid>
    # 5.搜索16进制显示的线程ID,定位到具体代码
    vim jstack.log
    # -A100是日志行数
    # jstack |grep 16进制线程号 -A100
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3.排查线程阻塞

    检查连接数
    netstat -nap | grep <pid>
    
    • 1

    四、GC调优

    • jmap:查看内存信息

      # 查看新生代老年代
      jmap -heap pid
      # 生成dump文件
      jmap -dump:live,format=b,file=/tmp/heapdump.hprof <pid>
      
      • 1
      • 2
      • 3
      • 4
    • Jstack:进程、线程、堆栈

      # 打印这个进程的所有线程的运行堆栈
      jstack -l <pid>
      # 输出heap的直方图,包括类名,对象数量,对象占用大小
      jmap -histo <pid>
      # 输出堆内存设置和使用情况(JDK11使用jhsdb jmap --heap --pid pid)
      jmap -heap <pid>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • Jinfo:查看jvm的参数

      Jinfo <pid>
      
      • 1
    • Jstat:堆内存各部分的使用量,以及加载类的数量

      # 输出gc信息,包括gc次数和时间,内存使用状况
      jstat -gc <pid>
      jstat -gcutil <pid>
      
      • 1
      • 2
      • 3
    • Jcmd:从JDK 7开始提供,jcmd拥有jmap的大部分功能,官方推荐使用jcmd命令替代jmap命令。

      # 列出所有的JVM进程
      jcmd -l 
      # 针对指定的进程,列出支持的所有命令
      jcmd <pid> help 
      
      • 1
      • 2
      • 3
      • 4
  • 相关阅读:
    Spring中还有一招集合注入的写法
    474922-22-0 DSPE-PEG-MAL Maleimide-PEG-DSPE 磷脂-聚乙二醇-马来酰亚胺
    Linux 系统管理工具 supervisor 详解
    MySQL的Redo log 、Undo log、 Binlog
    ovn metadata (by quqi99)
    iOS开发Swift-11-正向传值,搜索,反向传值,城市id获取天气,光标聚焦,拦截空白/空格字符-和风天气App次页代码
    吃透分享的这份 Java 面试神技,3 个月斩获 8 家 offer
    Springboot多数据源及事务实现方案
    c++怎么传递函数
    自上而下 or 自下而上?企业部署RPA的2种策略
  • 原文地址:https://blog.csdn.net/Duke147/article/details/125995950