Cpu消耗过高原因分析

  • 背景
  • 现象
  • 猜测导致服务器响应变慢的原因
  • 排查过程及结果
  • 猜测Cpu高消耗的原因
  • Top命令查看进程占用Cpu情况
  • Top -Hp 【pid】查看进程内线程占用Cpu情况
  • 发现异常线程占用Cpu情况
  • 堆栈分析
  • jmap、jhat
  • 使用命令
  • 导出jvm线程对应的堆栈信息
  • jstack 使用
  • 导出java进程中单个线程的轨迹
  • 在线分析
  • 可使用自动化脚本
  • 结果


背景

记录公司一次技术分享,也是一次生产服务器假死的解决过程。

现象

1.前端页面响应慢,用户无法正常工作。
2.大量接口客户端服务超时报警。

猜测导致服务器响应变慢的原因

1.QPS激增,数据库压力上涨导致请求阻塞
2.网络异常,大面积服务暂时不可用
3.OOM,导致某台服务不可用,影响上层消费者服务
4.服务器存储空间占满
5.CPU超负荷,请求被阻塞

排查过程及结果

通过df命令查看磁盘空间使用情况,排除服务器存储空间问题
通过free -h -s 3命令每隔3秒查看内存内存使用情况,排除问题
通过elk接口访问统计,排除QPS激增和OOM
最终通过top命令查看CUP使用情况,发现CPU使用情况消耗太高

猜测Cpu高消耗的原因

1.请求创建的线程过多,频繁切换上下文
2.大量Full gc
3.死循环
4.计算密集型任务被触发,比如计算圆周率、组合排队、视频编码等

Top命令查看进程占用Cpu情况

cpu内存占用 java cpu内存占用高是什么原因_cpu内存占用 java

Top -Hp 【pid】查看进程内线程占用Cpu情况

cpu内存占用 java cpu内存占用高是什么原因_cpu内存占用 java_02

发现异常线程占用Cpu情况

cpu内存占用 java cpu内存占用高是什么原因_cpu内存占用 java_03

堆栈分析

jmap、jhat

使用命令

使用 jmap -heap 【pid】 查看进程堆内存使用情况,包括使用的GC算法、堆配置参数和各代中堆内存使用情况

root@ubuntu:/# jmap -heap 21711
Attaching to process ID 21711, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.10-b01

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 2067791872 (1972.0MB)
   NewSize          = 1310720 (1.25MB)
   MaxNewSize       = 17592186044415 MB
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 21757952 (20.75MB)
   MaxPermSize      = 85983232 (82.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 6422528 (6.125MB)
   used     = 5445552 (5.1932830810546875MB)
   free     = 976976 (0.9317169189453125MB)
   84.78829520089286% used
From Space:
   capacity = 131072 (0.125MB)
   used     = 98304 (0.09375MB)
   free     = 32768 (0.03125MB)
   75.0% used
To Space:
   capacity = 131072 (0.125MB)
   used     = 0 (0.0MB)
   free     = 131072 (0.125MB)
   0.0% used
PS Old Generation
   capacity = 35258368 (33.625MB)
   used     = 4119544 (3.9287033081054688MB)
   free     = 31138824 (29.69629669189453MB)
   11.683876009235595% used
PS Perm Generation
   capacity = 52428800 (50.0MB)
   used     = 26075168 (24.867218017578125MB)
   free     = 26353632 (25.132781982421875MB)
   49.73443603515625% used
   ....

附列性能参数:

cpu内存占用 java cpu内存占用高是什么原因_服务器_04


cpu内存占用 java cpu内存占用高是什么原因_服务器_05

使用jmap -histo[:live] pid查看堆内存中的对象数目、大小统计直方图,如果带上live则只统计活对象

root@ubuntu:/# jmap -histo:live 21711 | more

 num     #instances         #bytes  class name
----------------------------------------------
   1:         38445        5597736  <constMethodKlass>
   2:         38445        5237288  <methodKlass>
   3:          3500        3749504  <constantPoolKlass>
   4:         60858        3242600  <symbolKlass>
   5:          3500        2715264  <instanceKlassKlass>
   6:          2796        2131424  <constantPoolCacheKlass>
   7:          5543        1317400  [I
   8:         13714        1010768  [C
   9:          4752        1003344  [B
  10:          1225         639656  <methodDataKlass>
  11:         14194         454208  java.lang.String
  12:          3809         396136  java.lang.Class
  13:          4979         311952  [S
  14:          5598         287064  [[I
  15:          3028         266464  java.lang.reflect.Method
  16:           280         163520  <objArrayKlassKlass>
  17:          4355         139360  java.util.HashMap$Entry
  18:          1869         138568  [Ljava.util.HashMap$Entry;
  19:          2443          97720  java.util.LinkedHashMap$Entry
  20:          2072          82880  java.lang.ref.SoftReference
  21:          1807          71528  [Ljava.lang.Object;
  22:          2206          70592  java.lang.ref.WeakReference
  23:           934          52304  java.util.LinkedHashMap
  24:           871          48776  java.beans.MethodDescriptor
  25:          1442          46144  java.util.concurrent.ConcurrentHashMap$HashEntry
  26:           804          38592  java.util.HashMap
  27:           948          37920  java.util.concurrent.ConcurrentHashMap$Segment
  28:          1621          35696  [Ljava.lang.Class;
  29:          1313          34880  [Ljava.lang.String;
  30:          1396          33504  java.util.LinkedList$Entry
  31:           462          33264  java.lang.reflect.Field
  32:          1024          32768  java.util.Hashtable$Entry
  33:           948          31440  [Ljava.util.concurrent.ConcurrentHashMap$HashEntry;

导出jvm线程对应的堆栈信息

1.jmap -dump:live,format=b,file=file.dat 262
备注:file.bin 表示文件名称,262表示jvm线程PID ,-dump:live 表示堆中存活的对象
2.用jhat查看
jhat -port 9998 /tmp/dump.dat

root@ubuntu:/# jhat -port 9998 /tmp/dump.dat
Reading from /tmp/dump.dat...
Dump file created Tue Jan 28 17:46:14 CST 2014
Snapshot read, resolving...
Resolving 132207 objects...
Chasing references, expect 26 dots..........................
Eliminating duplicate references..........................
Snapshot resolved.
Started HTTP server on port 9998
Server is ready.

登陆服务器即可

jstack 使用

导出java进程中单个线程的轨迹

jstack 262 >test.txt
备注:262 表示PID ,test.txt 表示导出文件

在线分析

1.查看十六进制pid
printf “%x\n” 【pid】
2.jstack 【pid】 |grep 【十六进制pid】

可使用自动化脚本

当服务器线程数超过 2500 时自动 dump 线程数最高的 java 进程的内存及线程栈。

#!/usr/bin/env bash
# 
# 服务器线程数达到 2500 以上时 dump 线程数最多的 java 进程的线程及内存
#
source ~/.bashrc
cur_thread_num=`ps -efL | wc -l`
if [ $cur_thread_num -le 2500 ]; then
    exit 0
fi

cur_date=`date +"%Y-%m-%d_%H-%M-%S"`
cd ./dumpfile
# 服务器当前线程 dump 到文件:按照线程数由大到小排序显示
ps -efL --sort -nlwp > server_thread_dump_$cur_date
# dump 线程数最多的 jvm 的线程及内存
most_thread_num_pid=`cat server_thread_dump_$cur_date | sed -n '2p' | awk '{print $2}'`
nohup jstack -l $most_thread_num_pid > java_app_thread_dump_${cur_date}_pid_${most_thread_num_pid} &
nohup jmap -dump:format=b,file=java_app_mem_dump_${cur_date}_pid_${most_thread_num_pid} $most_thread_num_pid &

exit 0

结果

通过分析堆栈,找到大量GC,分析GC原因,修改问题代码,解决问题