CPU占用过高的问题如何定位

    本文将介绍如何使用一些常用的调优工具来实现JVM调优。

    下面废话不多说,直接开干

一、准备

     为了具备jvm调优的场景,这里准备如下图这样一段简单的代码。

public class FullGCProblem {
    //线程池
    private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50,
            new ThreadPoolExecutor.DiscardOldestPolicy());

    public static void main(String[] args) throws Exception {
        //50个线程
        executor.setMaximumPoolSize(50);
        while (true){
            calc();
            Thread.sleep(100);
        }
    }
    //多线程执行任务计算
    private static void calc(){
        List<UserInfo> taskList = getAllCardInfo();
        taskList.forEach(userInfo -> {
            executor.scheduleWithFixedDelay(() -> {
                userInfo.user();
            }, 2, 3, TimeUnit.SECONDS);
        });
    }
    //模拟从数据库读取数据,返回
    private static List<UserInfo> getAllCardInfo(){
        List<UserInfo> taskList = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            UserInfo userInfo = new UserInfo();
            taskList.add(userInfo);
        }
        return taskList;
    }
    private static class UserInfo {
        String name = "zhangsan";
        int age = 18;
        BigDecimal money = new BigDecimal(999999.99);

        public void user() {
            //
        }
    }
}

       在工程的资源目录下,做如下配置

        

     使用maven打成jar包,放到linux环境下运行起来。

     启动命令如下:

java -cp JVM_Demo-v1.0.jar -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -Xms200M -Xmx200M com.hl.ex13.FullGCProblem

二、CPU 占用过高排查

1、先通过 top 命令找到消耗 cpu 很高的进程

       top 命令是我们在 Linux 下最常用的命令之一, 它可以实时显示正在执行进程的 CPU 使用率、 内存使用率以及系统负载等信息。 其中上半部分显示的是系统的统计信息, 下半部分显示的是进程的使用率统计信息。

                

     由上图中,可以看出,进程id为7139的进程cpu使用率偏高。

2、执行命令“ top -p 7139  -H”, 单独监视进程7139下的所有的线程信息,如下图

               

       从这个图中,可以看出,线程id为7141和7142两个线程占用cpu较高。

3、执行命令 “jstack 7139> jstack.txt” 命令,将线程的所有的信息做dump记录。完成后,将jstack.txt文件下载到本地,使用编辑器打开。

4、将2步中得到的线程id  7141使用计算器,转换成16进制。得到是0x1be5。

5、拿着这个转换后的数,去刚下载到本地的文本中去查找,如下图所示

     从这个图,就可以发现找是 VM 的线程占用过高。

6、这里使用jstat命令监控gc的情况

    命令如下:

jstat -gc 7139 5000 20 | awk '{print $13, $14,$15,$16,$17}'

    监视效果如下:

          

     从这个图中发现,YGC次数很少,而FGC就很多。也就是说JVM在拼命的做FULLGC。

7、接下来,还可以使用jmap命令,查看堆内存的情况

        命令如下:

jmap -histo 7139 | head -20

        监控如下图:

       从这个监控结果中,发现了堆中有很多这些的实例。

       由此不难得出,问题就出在我们的代码里了

        

          

      代码中不停地往线程池里面的放入对象。而从这个源码里面看出,最多线程数是线程的最大值,相当于是没有上限,而关键的是使用DelayedQueue队列,但它是一个无界队列。那就会出现一些来不及执行的任务所持有的对象在这个队列占位,这样一来这些对象也就没有办法回收掉。但是我们jar启动的时候,指定堆的最大值是200M,因此也就不难理解为什么会出现频繁的FULLGC了,因为对象持续得不到回收就进入了老年代,而老年代也是有大小限制的。

三、总结

在 JVM 出现性能问题的时候。 (表现上是 CPU100%, 内存一直占用)
1、 如果 CPU 的 100%, 要从两个角度出发, 一个有可能是业务线程疯狂运行, 比如说想很多死循环。 另一种可能性, 就是 GC 线程在疯狂的回收, 因为 JVM 中垃圾回收器主流也是多线程的, 所以很容易导致 CPU 的 100%
2、 在遇到内存溢出的问题的时候, 一般情况下我们要查看系统中哪些对象占用得比较多, 这里采用一个很简单的代码, 在实际的业务代码中, 找到对应的对象, 分析对应的类, 找到为什么这些对象不能回收的原因。涉及到的知识包括可达性分析算法, JVM 的内存区域, 还有垃圾回收器等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值