【JVM调优系列】----CPU过高的分析与解决方案

简介: 问题描述          服务器是8核32G的,也就是说同时可用的共有8个CPU,一个CPU可以使用高达100%,8个CPU的话可以高达800%。
问题描述

    
    服务器是8核32G的,也就是说同时可用的共有8个CPU,一个CPU可以使用高达100%,8个CPU的话可以高达800%。前两天发现了一个CPU过高的问题,平时项目运行CPU也就是在10%,但是前两天发布之后突然发现CPU一直在200%左右打转,一直稳高不降。下面的例子只是参考(当时的情况没有截图o(╯□╰)o)。执行top命令查看占用CPU高的进程。

top - 13:30:14 up 611 days,  2:06,  1 user,  load average: 6.77, 6.53, 6.34
Tasks: 137 total,   1 running, 136 sleeping,   0 stopped,   0 zombie
%Cpu(s): 74.8 us,  2.8 sy,  0.0 ni, 21.6 id,  0.0 wa,  0.0 hi,  0.8 si,  0.0 st
KiB Mem:  32783044 total, 32165440 used,   617604 free,   117916 buffers
KiB Swap:        0 total,        0 used,        0 free.  4187724 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                           
 6795 root      20   0 27.299g 0.021t   7160 S 625.9 70.1  22775:31 java                                                                                              
23631 root      20   0 1459736 1.258g    608 S   1.3  4.0   1535:06 redis-server                                                                                      
 8005 mysql     20   0 3417268 1.200g      0 S   0.0  3.8   1336:47 mysqld                                                                                            
    1 root      20   0   94680  56000   1312 S   0.0  0.2  15:10.94 systemd                                                                                           
32087 root      20   0  171148  10364   4192 S   1.0  0.0 134:42.99 AliYunDun                                                                                         
 4816 root      20   0   36792   7992   7708 S   0.0  0.0   9:03.10 systemd-journal                                                                                   
 9160 polkitd   20   0  524708   6388    836 S   0.0  0.0   3:33.12 polkitd                                                                                           
  678 root      20   0  725108   6036   5204 S   0.0  0.0   7:49.42 rsyslogd                                                                                          
32525 root      20   0  141228   5372   4108 S   0.0  0.0   0:00.16 sshd                                                                                              
32527 root      20   0  116544   3392   1780 S   0.0  0.0   0:00.04 bash                                                                                              
18526 root      20   0  116648   1684      4 S   0.0  0.0   0:00.28 bash                                                                                              

    

解决方案

    
    由上图我们可以看到Java进程占用的CPU高达600%,但具体是因为那些任务造成的在这是无法直接看出来的。

  •     一、打印线程堆栈信息 —- jstack命令

    jstack命令可用于导出Java应用程序的线程堆栈信息,可以使用重定向将输出保存到文件中。

jstack 6795 > jstack6795
"Low Memory Detector" daemon prio=10 tid=0x081465f8 nid=0x7 runnable [0x00000000..0x00000000]  
        "CompilerThread0" daemon prio=10 tid=0x08143c58 nid=0x6 waiting on condition [0x00000000..0xfb5fd798]  
        "Signal Dispatcher" daemon prio=10 tid=0x08142f08 nid=0x5 waiting on condition [0x00000000..0x00000000]  
        "Finalizer" daemon prio=10 tid=0x08137ca0 nid=0x4 in Object.wait() [0xfbeed000..0xfbeeddb8]  

        at java.lang.Object.wait(Native Method)  

        - waiting on <0xef600848> (a java.lang.ref.ReferenceQueue$Lock)  

        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116)  

        - locked <0xef600848> (a java.lang.ref.ReferenceQueue$Lock)  

        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132)  

        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)  

        "Reference Handler" daemon prio=10 tid=0x081370f0 nid=0x3 in Object.wait() [0xfbf4a000..0xfbf4aa38]  

        at java.lang.Object.wait(Native Method)  

        - waiting on <0xef600758> (a java.lang.ref.Reference$Lock)  

        at java.lang.Object.wait(Object.java:474)  

        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)  

        - locked <0xef600758> (a java.lang.ref.Reference$Lock)  

        "VM Thread" prio=10 tid=0x08134878 nid=0x2 runnable  

        "VM Periodic Task Thread" prio=10 tid=0x08147768 nid=0x8 waiting on condition 

    在打印的堆栈日志文件中,tidnid的含义:

nid : 对应的Linux操作系统下的tid线程号,也就是前面转化的16进制数字 
tid: 这个应该是jvm的jmm内存规范中的唯一地址定位

    在CPU过高的情况下,查找响应的线程,一般定位都是用nid来定位的。而如果发生死锁之类的问题,一般用tid来定位。

  •     定位CPU高的线程打印其nid

    查看线程下具体进程信息的命令如下:

top -H -p 6735 
top - 14:20:09 up 611 days,  2:56,  1 user,  load average: 13.19, 7.76, 7.82
Threads: 6991 total,  17 running, 6974 sleeping,   0 stopped,   0 zombie
%Cpu(s): 90.4 us,  2.1 sy,  0.0 ni,  7.0 id,  0.0 wa,  0.0 hi,  0.4 si,  0.0 st
KiB Mem:  32783044 total, 32505008 used,   278036 free,   120304 buffers
KiB Swap:        0 total,        0 used,        0 free.  4497428 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                            
 6800 root      20   0 27.299g 0.021t   7172 S 54.7 70.1 187:55.61 java                                                                                               
 6803 root      20   0 27.299g 0.021t   7172 S 54.4 70.1 187:52.59 java                                                                                               
 6798 root      20   0 27.299g 0.021t   7172 S 53.7 70.1 187:55.08 java                                                                                               
 6801 root      20   0 27.299g 0.021t   7172 S 53.7 70.1 187:55.25 java                                                                                               
 6797 root      20   0 27.299g 0.021t   7172 S 53.1 70.1 187:52.78 java                                                                                               
 6804 root      20   0 27.299g 0.021t   7172 S 53.1 70.1 187:55.76 java                                                                                               
 6802 root      20   0 27.299g 0.021t   7172 S 52.1 70.1 187:54.79 java                                                                                               
 6799 root      20   0 27.299g 0.021t   7172 S 51.8 70.1 187:53.36 java                                                                                               
 6807 root      20   0 27.299g 0.021t   7172 S 13.6 70.1  48:58.60 java                                                                                               
11014 root      20   0 27.299g 0.021t   7172 R  8.4 70.1   8:00.32 java                                                                                               
10642 root      20   0 27.299g 0.021t   7172 R  6.5 70.1   6:32.06 java                                                                                               
 6808 root      20   0 27.299g 0.021t   7172 S  6.1 70.1 159:08.40 java                                                                                               
11315 root      20   0 27.299g 0.021t   7172 S  3.9 70.1   5:54.10 java                                                                                               
12545 root      20   0 27.299g 0.021t   7172 S  3.9 70.1   6:55.48 java                                                                                               
23353 root      20   0 27.299g 0.021t   7172 S  3.9 70.1   2:20.55 java                                                                                               
24868 root      20   0 27.299g 0.021t   7172 S  3.9 70.1   2:12.46 java                                                                                               
 9146 root      20   0 27.299g 0.021t   7172 S  3.6 70.1   7:42.72 java   

    由此可以看出占用CPU较高的线程,但是这些还不高,无法直接定位到具体的类。nid是16进制的,所以我们要获取线程的16进制ID:

printf "%x\n" 6800 
输出结果:45cd

    然后根据输出结果到jstack打印的堆栈日志中查定位:

"catalina-exec-5692" daemon prio=10 tid=0x00007f3b05013800 nid=0x45cd waiting on condition [0x00007f3ae08e3000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000006a7800598> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2082)
        at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
        at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:86)
        at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:32)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:745)

    剩下的就是去代码中查找具体的问题了。
    
    

相关文章
|
1月前
|
消息中间件 Java 应用服务中间件
我是如何通过火焰图分析让应用CPU占用下降近20%的
分享作者在使用Arthas火焰图工具进行Java应用性能分析和优化的经验。
|
2月前
|
存储 监控 算法
jvm-性能调优(二)
jvm-性能调优(二)
|
1月前
线程CPU异常定位分析
【10月更文挑战第3天】 开发过程中会出现一些CPU异常升高的问题,想要定位到具体的位置就需要一系列的分析,记录一些分析手段。
65 0
|
3月前
|
Java 编译器 Linux
JVM/编译器/CPU,究竟谁是卧底?一个曾经困扰我一个月的 bug
任何复杂的系统都可能因为一个小小的疏漏而无法运转,本文记录了一个困扰作者一个月的 bug 最终拨云见日的过程。
|
11天前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
|
9天前
|
监控 Java 编译器
Java虚拟机调优实战指南####
本文深入探讨了Java虚拟机(JVM)的调优策略,旨在帮助开发者和系统管理员通过具体、实用的技巧提升Java应用的性能与稳定性。不同于传统摘要的概括性描述,本文摘要将直接列出五大核心调优要点,为读者提供快速预览: 1. **初始堆内存设置**:合理配置-Xms和-Xmx参数,避免频繁的内存分配与回收。 2. **垃圾收集器选择**:根据应用特性选择合适的GC策略,如G1 GC、ZGC等。 3. **线程优化**:调整线程栈大小及并发线程数,平衡资源利用率与响应速度。 4. **JIT编译器优化**:利用-XX:CompileThreshold等参数优化即时编译性能。 5. **监控与诊断工
|
20天前
|
存储 监控 Java
JVM进阶调优系列(8)如何手把手,逐行教她看懂GC日志?| IT男的专属浪漫
本文介绍了如何通过JVM参数打印GC日志,并通过示例代码展示了频繁YGC和FGC的场景。文章首先讲解了常见的GC日志参数,如`-XX:+PrintGCDetails`、`-XX:+PrintGCDateStamps`等,然后通过具体的JVM参数和代码示例,模拟了不同内存分配情况下的GC行为。最后,详细解析了GC日志的内容,帮助读者理解GC的执行过程和GC处理机制。
|
28天前
|
Arthas 监控 数据可视化
JVM进阶调优系列(7)JVM调优监控必备命令、工具集合|实用干货
本文介绍了JVM调优监控命令及其应用,包括JDK自带工具如jps、jinfo、jstat、jstack、jmap、jhat等,以及第三方工具如Arthas、GCeasy、MAT、GCViewer等。通过这些工具,可以有效监控和优化JVM性能,解决内存泄漏、线程死锁等问题,提高系统稳定性。文章还提供了详细的命令示例和应用场景,帮助读者更好地理解和使用这些工具。
|
1月前
|
监控 架构师 Java
JVM进阶调优系列(6)一文详解JVM参数与大厂实战调优模板推荐
本文详述了JVM参数的分类及使用方法,包括标准参数、非标准参数和不稳定参数的定义及其应用场景。特别介绍了JVM调优中的关键参数,如堆内存、垃圾回收器和GC日志等配置,并提供了大厂生产环境中常用的调优模板,帮助开发者优化Java应用程序的性能。
|
1月前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
45 3

热门文章

最新文章

相关实验场景

更多
下一篇
无影云桌面