当前位置:   article > 正文

Java cpu 监控 分析_java有什么分析cpu

java有什么分析cpu

Abstract

在这篇文章中我们会综合性的介绍如何监控JVM cpu,  thread 级别cpu, 以及如何通过JFR技术来分析JVM的CPU 问题.

 

如何获取CPU

这里我们会先介绍如何在进程内部获取JVM的CPU. 这里我们主要采用JVM 自带的JMX来实现对自己的监控.

获取整个系统的JVM cpu

可以通过调用mbean中的getProcessCpuTime方法来得到中的cputime. 

简单点来说就是:

(cputime2 - cputime1)/1000000/elapseTimeInMs/processorCount

完整代码如下:

  1. package cpu;
  2. import com.sun.management.OperatingSystemMXBean;
  3. import java.lang.management.ManagementFactory;
  4. import java.util.concurrent.Executors;
  5. import java.util.concurrent.ScheduledExecutorService;
  6. import java.util.concurrent.ThreadFactory;
  7. import java.util.concurrent.TimeUnit;
  8. import java.util.concurrent.atomic.AtomicInteger;
  9. public class CpuTest {
  10. public static void main(String[] args) {
  11. final AtomicInteger seq = new AtomicInteger(0);
  12. ScheduledExecutorService es = Executors.newScheduledThreadPool(20, new ThreadFactory() {
  13. @Override
  14. public Thread newThread(Runnable r) {
  15. Thread th = new Thread(r);
  16. th.setName("consumingthreads-" + seq.incrementAndGet());
  17. return th;
  18. }
  19. });
  20. for (int i = 0; i < 200; i++) {
  21. es.scheduleAtFixedRate(new ConsumingCpuTask(), 0, 10, TimeUnit.MILLISECONDS);
  22. }
  23. // not terminate the es
  24. // another thread to print host cpu
  25. ScheduledExecutorService printer = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
  26. @Override
  27. public Thread newThread(Runnable r) {
  28. Thread th = new Thread(r);
  29. th.setName("printer");
  30. return th;
  31. }
  32. });
  33. // print every 10 seconds
  34. printer.scheduleAtFixedRate(new PrintCurrentProcessCpuTask(), 0, 10, TimeUnit.SECONDS);
  35. }
  36. static final int PROCESSOR_COUNT = Runtime.getRuntime().availableProcessors();
  37. // notice here is com.sun.management.OperatingSystemMXBean and it's not java.lang.management.OperatingSystemMXBean
  38. static final OperatingSystemMXBean bean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
  39. /**
  40. * get process cpu in nanoseconds
  41. */
  42. static double getProcessCpuTime() {
  43. return bean.getProcessCpuTime();
  44. }
  45. /**
  46. * A task to simulate consuming cpu
  47. */
  48. static class ConsumingCpuTask implements Runnable {
  49. @Override
  50. public void run() {
  51. AtomicInteger integer = new AtomicInteger(0);
  52. for (int i = 0; i < 10000; i++) {
  53. integer.incrementAndGet();
  54. }
  55. }
  56. }
  57. static class PrintCurrentProcessCpuTask implements Runnable {
  58. double cpuTime = 0;
  59. long collectTime = 0;
  60. @Override
  61. public void run() {
  62. if (cpuTime == 0) {
  63. cpuTime = getProcessCpuTime();
  64. collectTime = System.currentTimeMillis();
  65. }
  66. else {
  67. double newCpuTime = getProcessCpuTime();
  68. long newCollectTime = System.currentTimeMillis();
  69. double cpu = (newCpuTime - cpuTime) / (newCollectTime - collectTime) / 1000_000 / PROCESSOR_COUNT;
  70. cpuTime = newCpuTime;
  71. collectTime = newCollectTime;
  72. System.out.println(String.format("Process cpu is: %.2f %%", cpu * 100));
  73. }
  74. }
  75. }
  76. }

当你运行这个代码就可以看到定时打出的cpu指标,比如在我的机器上是17%左右(本身有16核): 

而这个值跟系统显示也是一致的(mac端的Activity Monitor/top命令):

或者用Java自带的Jconsole 可以看(是JDK自带的工具, 在bin目录),运行jconsole

选择关注的进程:

 

获取系统中各个线程的CPU

这里我们会展示如何获取系统中各个线程的CPU 这个也很好统计.

获取线程的cpu主要通过ThreadMXBean获取:

  1. // it's com.sun.management.ThreadMXBean
  2. static ThreadMXBean threadMXBean = (ThreadMXBean) ManagementFactory.getThreadMXBean();
  3. static class PrintThreadCpuTask implements Runnable {
  4. // not consider thread safe here
  5. Map<Long, Long> threadId2CpuTime = null;
  6. Map<Long, String> threadId2Name = null;
  7. private long collectTime = 0;
  8. @Override
  9. public void run() {
  10. if (threadId2CpuTime == null) {
  11. threadId2CpuTime = new HashMap<>();
  12. threadId2Name = new HashMap<>();
  13. long threads[] = threadMXBean.getAllThreadIds();
  14. long cpuTimes[] = threadMXBean.getThreadCpuTime(threads);
  15. for (int i = 0; i < threads.length; i++) {
  16. threadId2CpuTime.put(threads[i], cpuTimes[i]);
  17. // get the thread name
  18. // maybe null, if not exists any more
  19. ThreadInfo info = threadMXBean.getThreadInfo(threads[i]);
  20. if (info != null) {
  21. threadId2Name.put(threads[i], info.getThreadName());
  22. }
  23. }
  24. collectTime = System.currentTimeMillis();
  25. }
  26. else {
  27. long threads[] = threadMXBean.getAllThreadIds();
  28. long cpuTimes[] = threadMXBean.getThreadCpuTime(threads);
  29. Map<Long, Long> newthreadId2CpuTime = new HashMap<>();
  30. for (int i = 0; i < threads.length; i++) {
  31. newthreadId2CpuTime.put(threads[i], cpuTimes[i]);
  32. }
  33. long newCollectTime = System.currentTimeMillis();
  34. threadId2CpuTime.entrySet().forEach(en -> {
  35. long threadId = en.getKey();
  36. Long time = en.getValue();
  37. Long newTime = newthreadId2CpuTime.get(threadId);
  38. if (newTime != null) {
  39. double cpu = (newTime - time) * 1.0d / (newCollectTime - collectTime) / 1000000L / PROCESSOR_COUNT;
  40. System.out.println(String.format("\t\tThread %s cpu is: %.2f %%", threadId2Name.get(threadId), cpu * 100));
  41. }
  42. threadId2CpuTime.put(threadId, newTime);
  43. });
  44. }
  45. }
  46. }

输出如下:

如何分析

什么JFR

JFR(Java flying recorder), 是java内置的一个性能数据采集器. 可以详细的获取JVM内部的状态和事件.

JFR分析示例

通过如下的命令激活JFR:

java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder cpu.CpuTest

运行后通过内置的JCMD就可以进行采集等操作(JCMD 还有其他命令比如JFR.check 等.)

这个文件可以通过JMC(Java  mission control)打开, 直接运行jmc即可 (也在jdk/bin下面):

在这里面就可以看到热点方法:

JFR本身还支持很多事件的采集包括GC/IO等.

Reference:https://docs.oracle.com/javacomponents/jmc-5-4/jfr-runtime-guide/about.htm

一个实际例子

在产品环境中发现的一个问题, 客户会周期的执行一些snmp数据采集任务集合A, 然后每隔一段时间又会执行另外的任务集合B(此时A也在同步执行), 发现此时B执行时, CPU比较高,  这是正常的因为任务B本身比较耗CPU, 但是发现任务B完成后, CPU依然很高, 并没有下降的趋势.

CPU图形如下:

1. 分析了任务A的成功率并没有变化.

2. 我们对比了前后的线程是否有增减, 找到了一个Windows selector 但是一查CPU 也没有明显的变化(~3%).

3. 最后我们用前面的脚本查询了前后两次的所有线程的CPU 发现了, snmp相关的线程cpu每个都从1.5% 增加到了 7%.

  1. 59 lm-collector-snmp-transport--4-1=1.61
  2. 60 lm-collector-snmp-transport--4-2=1.48
  3. 61 lm-collector-snmp-transport--4-3=1.56
  4. 62 lm-collector-snmp-transport--4-4=1.80
  5. 64 lm-collector-snmp-transport--4-5=1.64
  6. 65 snmp-selector=1.22
  7. 66 lm-collector-snmp-transport--4-6=1.41
  8. 67 lm-collector-snmp-transport--4-7=1.46
  9. 68 lm-collector-snmp-transport--4-10=1.77
  10. 69 lm-collector-snmp-transport--4-9=1.51
  11. 70 lm-collector-snmp-transport--4-8=1.88

4. 然后突然想起来了 好像我们底层是共享的snmp 发送线程, 然后又因为有流控. 所以总共10组线程会每隔10ms 做一个host的发送任务. 但是在任务B中我们新加进来了很多任务(每个host新加了一个任务,然后在这10个线程中),相当于以前有900个host, 我们的线程组就会: 每10ms 执行900 次任务了, 然后B任务执行时, 又加了900个任务进来就是每10ms执行1800个任务.

这个对ScheduleExecutorService来说可能是个不小的性能问题.

5. 验证猜想. 我们dump 2次JFR也发现了:

B任务执行前:

B任务执行后:

注意这个Context Switch count 被Double了, 还有ScheduledThreadPool中的siftDown方法也被调用更加频繁了.

这个我本来最开始发现这个每个snmp线程的samplecount变多了, 但是感觉没有多很多, 就没去管.

实际上发现有还是多了很多的. 

解决办法嘛, 还在思考之中~~~~

博客代码:https://github.com/gaoxingliang/goodutils/blob/master/src/cpu/CpuTest.java

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/319761
推荐阅读
相关标签
  

闽ICP备14008679号