JVM CPU飙升问题

排查线上 java 进程CPU 飙升的问题


背景
某天晚上突然在工作群里看到服务响应变慢,于是急忙登录服务器看一下应用状态。如下图应用进程的状态一直徘徊在100%左右,但是一个台机器的服务cpu占用率极低。
在这里插入图片描述

根据网上资料
https://blog.csdn.net/qq_33404395/article/details/86242263?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

经过一番折腾最后定位到问题的原因,在极端的情况下系统陷入了死循环,问题伪代码如下

int i = 0;
while(i < 10){
	Integer result = xxxx; //执行业务代码,获取返回结果
	if(result != null){
    	i ++;
    }
}

至此问题已经很明显,由于业务迭代变更,执行业务代码有可能变成null,所以陷入了死循环。

以下是自己在本机上写了个简单的demo,做出的一些总结。代码写了2个线程,其中一个线程陷入死循环状态。 由于本人是mac电脑 无法使用 top -H的命令(google了很久没找到对应的命令,无法定位进程里面的线程ID),因此使用docker作为虚拟机进行测试

package com.andy.jdk8.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
 * 模拟 cpu 飙升 test 类
 */
public class CpuTest {
    public static void main(String[] args) {
        boolean isTrue = true;
        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.execute(new Runnable() {
            @Override
            public void run() {
                while(true){
                    System.out.println("dead loop");
                }
            }
        });

        executor.execute(new Runnable() {
            @Override
            public void run() {
                for(int i = 0;i < 100000;i++){
                    try {
                        System.out.println("sleep loop");
                        TimeUnit.SECONDS.sleep(2);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        });
    }
}

步骤如下:

1、运行容器,将本机的class文件挂载到docker容器的/data目录下
docker run -itd --name java-cpu -v /Users/andy/tool/structure/structure/java/target/classes:/data java /bin/bash
2、进入容器,启动应用
docker exec -it java-cpu /bin/bash
cd /data
nohup java com.andy.jdk8.pool.CpuTest &
nohup 命令将程序作为后台进程运行
3、运行jcmd 命令,查看当前机器有多少java进行运行,如下图,前面的125就是进程id

在这里插入图片描述

4、找到进程中 占用资源率最高的线程 
     输入 top 125 ,然后按住 shift + h

在这里插入图片描述

4、可以看出140 这个线程ID,占用cpu的资源比较高,并将之转换成16进制
printf "%x\n" 140

在这里插入图片描述

5、定位线程代码,查看堆栈信息
jstack 125 |grep 8c -A 30
很明显问题出现在程序的第19行

在这里插入图片描述
6、我将排查的步骤写成了一个shell 脚本,以后直接运行就可以了。执行脚本的时候 代码应用名称,该脚本会查找应用进程里面占用资源cpu资源最高的线程堆栈,并输出到日志文件

pid=$(jcmd | grep $1 | awk '{print $1}')
echo "java process pid = $pid"

threadid=$(top -Hp $pid -d 2 -n 1 -b | awk 'NR==8{print $1}')

threadid0x=$(printf "%x\n" $threadid)

jstack $pid |grep $threadid0x -A 30 > log.txt

echo "execute success"

7、将文件内容名称成test.sh,执行 ./test.sh CpuTest . 执行的结果跟上面一致

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值