解决JVM的CPU使用率高

问题:每天的系统自动计算都会报CPU使用率过高短信报警与邮件报警

最终问题定位:vendor信息同步redis中代码 for循环中使用list.contains()线性查找判断sku是否存在,该代码块的时间复杂度近似O(n*m)导致线程并发情况下CPU耗用高

解决方案:把list数据放入HashSet判断sku是否存在,把CPU使用率从90+%降到了10+%及以下

通常的解决思路是:

  1. top命令找出有问题Java进程及其线程号:
    1. 开启线程显示模式
    2. CPU使用率排序
    3. 记下Java进程号及其CPU高的线程号
  2. 手动转成十六进制(可以用printf %x 1234)。
  3. jstack有问题的Java进程。
  4. grep十六进制的线程id,找到线程栈,查看关联的问题代码块。

     参见:https://github.com/oldratlee/useful-scripts/blob/master/docs/java.md#beer-show-busy-java-threadssh

本次解决时,基本知道哪个代码模块Job有问题,同时无法访问线上系统环境(可以申请),所以省略了大部分步骤,只使用了的jstack来解决问题。

本次解决思路:

1、根据报警时间与当时正在跑的Job,定位到具体JOB,当时只有VENDOR一个Job在跑

解决JVM的CPU使用率高

解决JVM的CPU使用率高

2、晚上跑vendor信息同步redis,在cpu使用率高时,选择一台机器,执行 jstack -l  pid命令,并导出栈信息

3、查看栈信息,找到具体的线程栈,查看当时栈都在执行什么操作,发现vendor的10个线程所有Thread栈都在执行com.calc.redis.adaptor.VendorSKURedisAdaptor.getValue(VendorSKURedisAdaptor.java:66)

解决JVM的CPU使用率高

4、分析具体的代码,基本定位到使用list.contains()线性查找导致CPU使用过高

解决JVM的CPU使用率高

5、将上段代码由list改为HashSet,上线测试,CPU使用率正常,问题解决。如果CPU还高的话就需要循环以上步骤,改进耗CPU的代码

Tips:解决问题过程中发现的一个有关线程池的未合理使用场景

定位到具体job,分析相应时间段的任务队列表时,发现CPU高集中报在Job会连续占用多个id相连的任务阶段。线程池固定大小是10,队列固定也是10,RejectedExecutionHandler策略使用的是CallerRunsPolicy,当CallerRunsPolicy生效跑的过程中,线程池不会放入新的任务,使得CallerRunsPolicy执行完后线程池队列基本是空的,未能充分使用线程资源。

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 10, 120, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10), new ThreadPoolExecutor.CallerRunsPolicy());

这时需要考虑如何合理的使用线程资源,诸位自己想想吧。

相关推荐