• JVM线上问题排查


    前言

    本文介绍服务器内运行的 Java 应用产生的 OOM 问题 和 CPU 100% 的问题定位

    1. 内存 OOM 问题定位

    某Java服务(比如进程id pid 为 3320)出现OOM,常见的原因为:

    • 内存分配的确实小了,而正常业务使用了大量的内存
    • 某个对象被频繁申请,却没有释放,内存不断泄露,导致内存耗尽
    • 某个资源被频繁申请,系统资源耗尽。例如不断创建线程,不断发起网络请求。

    资源不够(也是"给的资源耗尽"),资源申请过多导致资源耗尽,资源申请过多不释放导致资源耗尽。

    以下为使用工具排查方法:

    1.1 jmap 确认内存是否分配过小

    命令:jmap -heap 3320

    可查看新生代,老年代堆内部内存的分配大小以及使用情况。看是否是因为分配的过小

    jvm-jmap-堆信息

    1.2 找到最耗内存的对象

    jmap -histo:live 3320 | more

    jmap统计占用内存情况

    [C is a char[]
    [S is a short[]
    [I is a int[]
    [B is a byte[]
    [[I is a int[][]
    

    会以表格的形式显示存活对象的信息,并按内存大小排序。(num:排名,instances:实例数,bytes:所占内存大小,class name: 类名)上面的输出中[C对象占用Heap这么多,往往跟String有关,String其内部使用final char[]数组来保存数据的。

    对于实例数较多,占用内存大小较多的实例/类,相关的代码就要针对性review了。上图占用最多的是[C 占用30M。

    如果发现某类对象占用内存很大(例如几个G),很可能是类对象创建太多,且一直未释放。例如:

    • 申请完资源后,未调用close()或dispose()释放资源
    • 消费者消费速度慢(或停止消费了),而生产者不断往队列中投递任务,导致队列中任务累积过多

    线上执行该命令会强制执行一次fgc。另外还可以dump内存进行分析。

    1.3 确认是否是资源耗尽

    工具:

    • pstree
    • netstat

    查看进程创建的线程数,以及网络连接数,如果资源耗尽,也可能出现OOM。

    这里介绍另一种方法,通过

    • /proc/${PID}/fd
    • /proc/${PID}/task
      可以分别查看句柄详情和线程数。

    fd
    task

    如上图,sshd共占用了四个句柄

    • 0 -> 标准输入
    • 1 -> 标准输出
    • 10 -> 标准错误输出
    • 100 -> socket(容易想到是监听端口)

    文件描述符fd。linux中, 每一个进程在内核中,都对应有一个“打开文件”数组,存放指向文件对象的指针,而 fd 是这个数组的下标。 我们对文件进行操作时,系统调用,将fd传入内核,内核通过fd找到文件,对文件进行操作。
    fd作为数组下标,fd的类型为int, < 0 为非法值, >=0 为合法值。在linux中,一个进程默认可以打开的文件数为1024个,fd的范围为0~1023。可以通过设置,改变最大值。在linux中,值为0、1、2的fd,分别代表标准输入、标准输出、标准错误输出。

    • ll /proc/${PID}/fd | wc -l
    • ll /proc/${PID}/task | wc -l (效果等同pstree -p | wc -l)
      就能知道进程打开的句柄数和线程数。

    wcl

    2. CPU 100% 问题定位

    线上服务器中如果多实例部署,如何定位是哪个服务进程导致CPU过载,哪个线程导致CPU过载,那段代码导致CPU过载?

    大致步骤如下:

    1. 找到最耗CPU 的进程
    2. 找到最耗CPU 的线程
    3. 查看堆栈,定位线程在干嘛,定位对应代码

    2.1 找到最耗 CPU 的进程

    使用 top 命令

    1. 执行 top -c 查看进程运行信息列表
    2. 输入P(大写) 进程按照CPU 使用率排序

    好像top后就是按cpu使用率来排序的。

    cpu100

    如上图 最耗CPU的进程PID是7199

    2.2 找到最耗 CPU 的线程

    使用 top命令

    1. top -Hp 7199 显示一个进程的线程运行信息
    2. 输入大写P ,线程按照CPU使用率排序

    thread100

    如上图 进程 7199 中 最耗CPU的线程PID为7248

    2.3 查看堆栈 定位对应代码

    1. 将线程PID 转化为16进制

    使用 printf "%x " 7248

    返回 1c50

    1. 参看堆栈,找到线程在干嘛

    使用 jstack 7199 | grep '0x1c50' -C5 --color

    • 打印进程堆栈
    • 通过线程id 过滤得到线程堆栈

    cpu100Ajava
    如上图 耗CPU 最高的线程对应的线程名称 "Thread-7" 以及相应的代码 A.java

    1. 根据堆栈信息,找到相应的代码

    3. 其他方法

    上面的方法比较原始,并且比较繁琐,一般使用现有的轮子

    • arthas 快速高效

      • 在使用 Arthas 之前,当遇到 Java 线上问题时,如 CPU 飙升、负载突高、内存溢出等问题,你需要查命令,查网络,然后 jps、jstack、jmap、jhat、jstat、hprof 等一通操作。最终焦头烂额,还不一定能查出问题所在。而现在,大多数的常见问题你都可以使用 Arthas 轻松定位,迅速解决,及时止损,准时下班。
      • Arthas 是 Alibaba 在 2018 年 9 月开源的 Java 诊断工具。
    • show busy java thread 脚本

      • 将上面的步骤进行脚本封装,执行脚本直接得出结果,目前仅在linux上使用
      • link
    • jmc

      • jdk bin目录下工具,可对应用进行监控

    References

  • 相关阅读:
    程序猿财务自由之路·规划篇
    如何教女友学编程?
    粤港澳大湾区9城最新购房政策一览
    一个漂亮妹子的美团面试经历,4轮2小时,成功拿到Offer
    在北京的互联网公司工作多少年可以买房?
    这六个 MySQL 死锁案例,能让你理解死锁的原因!
    阿里双11:「线上全链路压测」完整经验分享
    千亿级公司低代码平台的测试体系介绍
    Weblogic
    30分钟?不需要,轻松读懂IL
  • 原文地址:https://www.cnblogs.com/wei57960/p/13213237.html
Copyright © 2020-2023  润新知