运维Bug追踪性能测试

服务端性能问题排查及优化---CPU高问题分析

2019-02-14  本文已影响148人  土司阿哈

CPU高是常见的性能问题,但CPU高并不一定都是有问题,有可能你的业务就是CPU密集型的业务。
如果CPU资源有限的情况下,本文可以提供一下优化CPU使用的方法,可以尽可能的优化,使CPU在可能的范围内降到最低。

可能造成CPU高的情况 :

可能导致CPU高的情况

  1. 代码Bug
    可能性太多…
  2. 意外的死循环
    手抖写的死循环或者计算失误导致死循环
  3. 线程数太多(线程数量是否比预期的异常的高)
    大量的线程切换:线程数太多可能会导致频繁的线程上下文的切换,浪费CPU资源。
    频繁创建销毁线程:线程的创建和销毁对系统的资源消耗比较大,如果一直在频繁的创建销毁临时线程导致资源占用,可能需要考虑下是否有其他更好的方案了。
    线程池不正常的使用:比如Cache线程池初始化比较少、最小和最大的数量相差比较大、业务并发突然增大可能导致同时去创建线程。
  4. 频繁的FGC
    导致频繁的FGC也会导致CPU高,比如由于JVM参数设置的不合理,年轻代和老年代的比例比例不合理导致频繁的FGC等。
  5. 不正常的使用某些类
    比如Map的初始大小给的太小,而后续的使用中存的东西太多,可能会频繁的resize。
    比如大数据量List的频繁查找,clone等。
    比如可重复利用资源的频繁初始化操作。

分析过程

用到的工具和命令

jvisualvm  
jstack,jstat,jmap,ps,top

CPU高的集中情况

分析方法

案例分析

案例介绍

测试时发现使用的的测试客户端CPU高,表现为运行一段时间后,CPU突然100%,出现该情况后不能恢复正常。

分析过程

  1. 首先抓取客户端的线程堆栈,看看能否发现什么可以的地方。
  2. 分析堆栈发现线程有1000多,大部分为BLOCKED状态,ACTIVE状态基本看到的都是nio的,暂时没看到问题。
  3. 继续搜索本地package的以下名称,看看有没有在执行自己代码的地方,正好发现一些类似以下的信息。
Thread 1134: (state = IN_NATIVE)
 - java.net.NetworkInterface.getAll() @bci=0 (Compiled frame; information may be imprecise)
 - java.net.NetworkInterface.getNetworkInterfaces() @bci=0, line=334 (Compiled frame)
 - com.alibaba.rocketmq.remoting.common.RemotingUtil.getLocalAddress() @bci=0, line=112 (Compiled frame)
 - com.alibaba.rocketmq.client.ClientConfig.<init>() @bci=19, line=32 (Compiled frame)
 - com.alibaba.rocketmq.client.producer.DefaultMQProducer.<init>(java.lang.String, com.alibaba.rocketmq.remoting.RPCHook) @bci=1, line=95 (Compiled frame)
 - com.alibaba.rocketmq.client.producer.DefaultMQProducer.<init>(java.lang.String) @bci=3, line=86 (Compiled frame)
 - ********************MQProducer.<init>(java.lang.String, java.lang.String) @bci=71, line=62 (Compiled frame)
 - ********************.RocketMQ.sendMessage() @bci=76,line=119 (Compiled frame)     // 119为源代码行号
 - ********************.RocketMQ$1$1.safeRun() @bci=7, line=53 (Compiled frame)
 - ********************.SafeRunnable.run() @bci=1, line=13 (Compiled frame)
 - java.util.concurrent.ThreadPoolExecutor.runWorker(java.util.concurrent.ThreadPoolExecutor$Worker) @bci=95, line=1145 (Compiled frame)
 - java.util.concurrent.ThreadPoolExecutor$Worker.run() @bci=5, line=615 (Interpreted frame)
 - java.lang.Thread.run() @bci=11, line=745 (Interpreted frame)

发现这些线程都在执行java.net.NetworkInterface.getAll() ,此方法比较耗费CPU,之前遇到过类似案例。接着分析为什么这几个线程会卡到这。

  1. 根据代码分析都在执行这个操作的原因。
public void sendMessage() {
    try {
        // 略…
        Message msg = new Message("Performace", msgContent.getBytes("UTF-8"));
        if (producer == null) {
            producer = new MQProducer("Performace", "192.168.143.135:9876");      (1)
            producer.start();
            rst = producer.product(msg);
        } else {
            rst = producer.product(msg);
        }
        // 略…
    } catch (Exception e) {
        if (producer != null) {
            producer.shutdown();
            producer = null;
        }
    }
}

sendMessage方法会被随机的注册到一个timer线程池上,有可能会在同一时间点或者很近时间点同时执行该方法。
producer.product(msg);为给远端发送信息,如果因为网络原因或者其他未知原因导致Exception,会把producer赋值为null,当再次执行sendMessage会重新初始化producer,如果恰好有多线程并发执行sendMessage,可能会导致重复初始化以及其他并发问题,导致恶性循环,恰好这个过程对CPU消耗比较多。

最后

以上是一个工作上简单案例的分析过程,实际工作中遇到的问题可能会复杂的多,过程可能会更曲折,需要从更多的方面去了解被测对象,甚至需要比开发自己更了解整个系统的架构,才能从多个方面去考虑问题,查找问题的真正原因。
本文由郭军英提供
2019年连续五十三天修心 土司于北京

上一篇下一篇

猜你喜欢

热点阅读