JVM问题定位


前言

在工作中,我们有可能在sit开发环境或者生产环境遇到JVM OOM的问题或者某个JAVA进程的CPU使用率居高不下。这个时候就需要我们通过对JVM的分析找到问题点。一般要么是JVM启动参数设置不到位或者我们的代码中出现了bug(如果是代码中出现bug,这个难度就比较大了)。下面我们就分两个方面描述如何定位到问题,一个是Java进程CPU居高不下,另一个是OOM的问题


一、Java进程CPU使用率居高不下

如果CPU使用率居高不下,一般的情况是我们的代码中出现了死锁的现象。下面我们自己写一个线程死锁的问题,然后看下如何定位问题。
死锁代码示例

public class DeadLock {

    private static Object object = new Object();

    public static void getLock() {
        synchronized (object) {
            while (true) {
                //获取到锁的线程在这里死循环不释放锁
            }
        }
    }

    public static void main(String[] args) {
        new Thread(() -> {
            getLock();
        }).start();

        new Thread(() -> {
            getLock();
        }).start();

        new Thread(() -> {
            getLock();
        }).start();
        System.out.println("---------end-------------------");
    }
}

在linux对代码进行javac编译和java运行,发现cpu飙高
top命令下查看
在这里插入图片描述

查看线程死锁问题,一般我们使用jstack命令进行查找
先通过jps -lm找到我们需要找的java进程

[root@localhost ~]# jps -lm
41298 sun.tools.jps.Jps -lm
40296 DeadLock

所以我们需要对进程40296 进行进一步观察

jstack 40296

由下图可以知道是因为线程死锁了,因为两个线程状态为Blocked的线程出现了waiting on <0x00000000c5008ee0>
在这里插入图片描述

我们可以过滤下关键之wait直接找下是否是因为死锁,实际定位中,我们通过该命令会打出一大堆信息,让我们在linux屏幕上一个一个找,太慢,我经常把信息复制到windows的txt文档,然后ctl f 搜索"wait"一个一个查找定位。由上图我们可以知道在代码的第7行出现了死锁现象,所以我们需要到对应代码第7行附近看下是不是锁没有释放。

二、OOM问题

这个就比较麻烦了,我们得分两种情况来进行阐述如何进行定位问题。一个是我们自己的开发环境,一种是生产环境。我们需要说明的是,在生产环境上能够进行的JVM定位问题的方法在开发环境是适用的。但是开发环境上的方法是不一定适用生产环境的。理由是我们可以在开发环境上直接把内存dump下来进行分析,但是生产上需要慎重考虑了,除非你们环境做了高可用,这个机器挂了不影响业务正常运行。

我们先给出一个有问题的代码示例
我们写一个不断,往list加对象,导致OOM

import java.util.ArrayList;
import java.util.List;
public class OOM {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        while (true){
            Object object = new Object();
            list.add(object);
        }
    }
}

java -Xms10M -Xmx10M -XX:+PrintGC OOM

为了快速报错OOM。我设置堆大小为10M,在接下来的测试中我将堆设置为200M,这样我们才有时间进行定位排查
在这里插入图片描述

2.1 开发环境定位OOM

方式一:
使用jdk原生命令进行查看

# 该命令能够在线查看jvm中占内存前20个最多的对象是哪些
# 这个命令对机器服务性能是有一些影响,但是影响不大,所以可以在生产上直接使用
 jmap -histo 63903 | head -20

通过观察可以看到Object对象一直在上涨
在这里插入图片描述

方式二:
将内存对象dump出来,然后在jconsole或者jvisual工具打开观察

# 该命令会把jvm在内存的对象都dump 出来,不要在生产上执行,因为一直jvm在内存中对象很大,这个是耗时操作,影响业务
jmap -dump:format=b,file=OOM 63903

在这里插入图片描述

然后将OOM这个文件下载到windows使用jdk自带的工具进行查看,这里举例使用jvisualvm进行查看
jvisualvm在你java_home的bin目录下
双击打开后。点击文件装入,选择堆dump,将刚刚的OOM文件导入
在这里插入图片描述

在这里插入图片描述
打开后就是这个界面,一般我会到下面的 “类标签”,看下大体哪些类的实例偏多,
在这里插入图片描述

通过观察,我们知道Object对象偏多,这个时候,就需要我们去代码中查找了,这就要求你对代码非常熟悉。定位问题还好,但是需要从代码找到bug,这个才是最难的。
在这里插入图片描述

当然我们可以打开OQL控制台,通过类似sql的语句进一步查询。如果需要了解,可以去网上找找看

2.2生产环境定位OOM

生产环境和开发环境不同,很多时候,我们不能直接将jvm内存中的java对象dump出来。这里有几种方式推荐
第一种方式

# 在java启动程序加入该参数,意味着当java程序发生OOM的时候,会在进程挂掉之前,把jvm当前的内存对象保存起来
 -XX:+HeapDumpOnOutOfMemoryError 

第二种方式
在程序发生OOM时候,让运维先别把服务重启,而是马上执行jmap -dump:format=b,file=xxx pid把内存对象导出来。一般这种方式不太推荐,因为一般生产需要马上恢复。但是如果可以dump的话,建议直接dump

第三种方式
将产生OOM的机器执行jmap -dump:format=b,file=xxx pid,但是前提是该服务做了高可用,我们挂了这个台机器不影响正整个系统使用

2.3 full gc 情况查看

通常运行命令如下:
jps -lm查看对应java进程pid
jstat -gc 6 3000
即会每5秒一次显示进程号为6的java进成的GC情况,
显示内容如下图: 显示的有点错位

在这里插入图片描述

S0C、S1C、S0U、S1U:Survivor 0/1区容量(Capacity)和使用量(Used)
EC、EU:Eden区容量和使用量
OC、OU:年老代容量和使用量
PC、PU:永久代容量和使用量
YGC、YGT:年轻代GC次数和GC耗时
FGC、FGCT:Full GC次数和Full GC耗时
GCT:GC总耗时

总结

JVM问题定位主要分为CPU线程锁或者JVM的OOM问题。比较难的是OOM问题的定位,因为还需要区分是不是生产环境。并且即使问题定位出来,还要找到具体哪一行代码有问题,这个是最难的。需要我们对自己的业务代码非常熟悉不然很难定位。在实际工作中,我们有的同事问题是定位到了,但是找不到具体是哪一行代码,因为里面包含很多第三方类库,不知道哪个地方有问题。所以后面只能使用重启大法了。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值