BruceOuyang / issuelist

用于记录日常碰到的各种问题和经验总结 (请看Issues)
4 stars 2 forks source link

JVM性能监控分析工具 jps、jstack、jmap、jhat、jstat、hprof 简单使用示例 #101

Open BruceOuyang opened 2 years ago

BruceOuyang commented 2 years ago
command remark
jps 主要用来输出JVM中运行的进程状态信息,全称 Java Virtual Machine Process Status Tool
jstack 主要用来查看某个Java进程内的线程堆栈信息,使用率很高
jmap 用来查看堆内存使用状况,一般结合jhat使用
jhat 用来分析jmap导出的数据信息
jstat JVM统计监测工具
hprof 能够展现CPU使用率,统计堆内存使用情况
BruceOuyang commented 2 years ago

jps 使用

1、语法

jps [options] [hostid]

options

-q 不输出类名、Jar名和传入main方法的参数
-m 输出传入main方法的参数
-l 输出main类或Jar的全限名
-v 输出传入JVM的参数

hostid

如果不指定hostid就默认为当前主机或服务器

2、示例 纯输出

[root@localhost ~]# jps
79426 Jps
44639 dongsee.jar

带参数

[root@localhost ~]# jps -l -m -v
79307 sun.tools.jps.Jps -l -m -v -Dapplication.home=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.el7_9.x86_64 -Xms8m
44639 /opt/apps/dongsee/dongsee.jar --spring.profiles.active=dev

第一列的数字为进程id,即其他命令需要用到的 pid

BruceOuyang commented 2 years ago

jstack 使用

1、语法

jstack [option] pid
jstack [option] executable core
jstack [option] [server-id@]remote-hostname-or-ip

options

-l long listings,会打印出额外的锁信息,在发生死锁时可以用jstack -l pid来观察锁持有情况
-m mixed mode,不仅会输出Java堆栈信息,还会输出C/C++堆栈信息(比如Native方法)

2、示例:找出某个Java进程中最耗费CPU的Java线程并定位堆栈信息,一般步骤:

可根据线程状态统计排序

grep java.lang.Thread.State jstack.44639 | awk '{print $2$3$4$5}' | sort | uniq -c

"DestroyJavaVM" #60 prio=5 os_prio=0 tid=0x00007fe5e004b800 nid=0xae60 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE

Locked ownable synchronizers:

BruceOuyang commented 2 years ago

jmap(Memory Map) 和 jhat(Java Heap Analysis Tool)的使用

1、语法

jmap [option] pid
jmap [option] executable core
jmap [option] [server-id@]remote-hostname-or-ip

2、示例

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

Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 2197815296 (2096.0MB) NewSize = 46137344 (44.0MB) MaxNewSize = 732430336 (698.5MB) OldSize = 92274688 (88.0MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB)

Heap Usage: PS Young Generation Eden Space: capacity = 413138944 (394.0MB) used = 135221512 (128.95728302001953MB) free = 277917432 (265.04271697998047MB) 32.730274878177546% used From Space: capacity = 12582912 (12.0MB) used = 12513352 (11.933662414550781MB) free = 69560 (0.06633758544921875MB) 99.44718678792317% used To Space: capacity = 19398656 (18.5MB) used = 0 (0.0MB) free = 19398656 (18.5MB) 0.0% used PS Old Generation capacity = 182976512 (174.5MB) used = 77980528 (74.36802673339844MB) free = 104995984 (100.13197326660156MB) 42.61778036297905% used

50915 interned Strings occupying 5130928 bytes.


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

[root@localhost jvm]# jmap -histo 44639 num #instances #bytes class name

1: 848943 81145048 [C 2: 771442 24686144 java.util.concurrent.locks.AbstractQueuedSynchronizer$Node 3: 70155 14315008 [B 4: 91315 14074080 [I 5: 551817 13243608 java.lang.String 6: 141630 12463440 java.lang.reflect.Method 7: 168286 9054200 [Ljava.lang.Object; 8: 139992 4479744 java.util.concurrent.ConcurrentHashMap$Node 9: 45002 3483720 [Ljava.util.HashMap$Node; 10: 84402 3376080 java.util.LinkedHashMap$Entry 11: 148810 3194464 [Ljava.lang.Class;

 class name是对象类型,说明如下

B byte C char D double F float I int J long Z boolean [ 数组,如[I表示int[] [L+类名 其他对象


* 3)用jmap把进程内存使用情况dump到文件中,再用jhat分析查看
先 jmap dump 

jmap -dump:format=b,file=/tmp/dump.dat 44639

后 jhat 查看

jhat -port 7001 /tmp/dump.dat


> port 可以自定义,访问 http://ip:7001 即可查看 jhat 的分析结果
BruceOuyang commented 2 years ago

jstat

follow https://my.oschina.net/feichexia/blog/196575

BruceOuyang commented 2 years ago

性能分析系列还可以参考:https://www.javatang.com/archives/2017/10/19/33151873.html

BruceOuyang commented 2 years ago

jstack dump 下来的文件统计线程状态封装脚本 thread-state-analysis.sh

#/bin/bash

java_home=/home/java8
dump_home=/home/dump

echo -n "Please Enter pid: "
read pid

$java_home/bin/jstack $pid > $dump_home/$pid
echo "========================================"

echo "Thread States in "$dump_home/$pid
echo "========================================"

grep java.lang.Thread.State $dump_home/$pid | awk '{print $2$3$4$5}' | sort | uniq -c
echo "========================================"

exit 0

jstack dump 下来的文件都放到 /home/jstack-dump 目录下,或自行修改 JD_HOME 配置

执行输出示例:

[root@appserver jstack-dump]# ./thread-state-analysis.sh
Please Enter pid: 9595
========================================
Thread States in /home/jstack-dump/9595
========================================
     12 RUNNABLE
     12 TIMED_WAITING(onobjectmonitor)
      3 TIMED_WAITING(parking)
     10 TIMED_WAITING(sleeping)
      2 WAITING(onobjectmonitor)
     16 WAITING(parking)
BruceOuyang commented 2 years ago

利用 jps 显示 pid 可封装为脚本 pid.sh

#/bin/bash
java_home=/home/java8
if [ ! $1 ]; then
  $java_home/bin/jps
else
  $java_home/bin/jps | grep $1 | awk '{print $1}'
fi
exit 0

使用示例:

[root@appserver dump]# ./pid.sh
6840 app.jar
10090 Jps

或(确保参数唯一,可传入应用 jar 名称)

[root@appserver dump]# ./pid.sh app.jar
6840
BruceOuyang commented 2 years ago

比较有用的命令

1)查看系统当前网络连接数

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

2)查看堆内对象的分布 Top 50(定位内存泄漏)

jmap –histo:live $pid | sort-n -r -k2 | head-n 50

3)按照 CPU/内存的使用情况列出前 10 的进程

#内存
ps axo %mem,pid,euser,cmd | sort -nr | head -10
#CPU
ps -aeo pcpu,user,pid,cmd | sort -nr | head -10

4)显示系统整体的 CPU 利用率和闲置率

grep "cpu " /proc/stat | awk -F ' ' '{total = $2 + $3 + $4 + $5} END {print "idle \t used\n" $5*100/total "% " $2*100/total "%"}'

5)按线程状态统计线程数(加强版)

jstack $pid | grep java.lang.Thread.State:|sort|uniq -c | awk '{sum+=$1; split($0,a,":");gsub(/^[ \t]+|[ \t]+$/, "", a[2]);printf "%s: %s\n", a[2], $1}; END {printf "TOTAL: %s",sum}';

6)查看最消耗 CPU 的 Top10 线程机器堆栈信息

推荐大家使用 show-busy-java-threads 脚本,该脚本可用于快速排查 Java 的 CPU 性能问题(top us 值过高),自动查出运行的 Java 进程中消耗 CPU 多的线程,并打印出其线程栈,从而确定导致性能问题的方法调用,该脚本已经用于阿里线上运维环境。链接地址:https://github.com/oldratlee/useful-scripts

7)火焰图生成(需要安装 perf、perf-map-agent、FlameGraph 这三个项目):

# 1. 收集应用运行时的堆栈和符号表信息(采样时间30秒,每秒99个事件);
sudo perf record -F 99 -p $pid -g -- sleep 30; ./jmaps

# 2. 使用 perf script 生成分析结果,生成的 flamegraph.svg 文件就是火焰图。
sudo perf script | ./pkgsplit-perf.pl | grep java | ./flamegraph.pl > flamegraph.svg

8)按照 Swap 分区的使用情况列出前 10 的进程**

for file in /proc/*/status ; do awk '/VmSwap|Name|^Pid/{printf $2 " " $3}END{ print ""}' $file; done | sort -k 3 -n -r | head -10

9)JVM 内存使用及垃圾回收状态统计

#显示最后一次或当前正在发生的垃圾收集的诱发原因
jstat -gccause $pid

#显示各个代的容量及使用情况
jstat -gccapacity $pid

#显示新生代容量及使用情况
jstat -gcnewcapacity $pid

#显示老年代容量
jstat -gcoldcapacity $pid

#显示垃圾收集信息(间隔1秒持续输出)
jstat -gcutil $pid 1000

10)其他的一些日常命令

# 快速杀死所有的 java 进程
ps aux | grep java | awk '{ print $2 }' | xargs kill -9

# 查找/目录下占用磁盘空间最大的top10文件
find / -type f -print0 | xargs -0 du -h | sort -rh | head -n 10

from: https://www.infoq.cn/article/istz0xt4vym6xxtx1fx1

BruceOuyang commented 2 years ago

SpringBoot 应用启动 JVM 参数配置参考

启动脚本 startup.sh

#/bin/bash
app_dc_home=/opt/app/dc
nohup java \
 -XX:+PrintGC \
 -XX:+PrintGCDetails \
 -XX:+PrintGCTimeStamps \
 -Xloggc:$app_dc_home/logs/gc.log \
 -Xmx500M \
 -Xms500M \
 -Xmn200M \
 -XX:CompressedClassSpaceSize=100M \
 -XX:MetaspaceSize=200M \
 -XX:MaxMetaspaceSize=200M \
 -jar $app_dc_home/app.jar --spring.profiles.active=prod >$app_dc_home/logs/nohup.out 2>&1 &
参数配置 说明
-XX:+PrintGC 打印 gc 信息
-XX:+PrintGCDetails 打印 gc 详细信息
-XX:+PrintGCTimeStamps 打印 gc 耗时信息
-Xloggc:$app_dc_home/logs/gc.log gc 信息输出到指定文件
-Xmx500M 设置最大堆内存为 500MB
-Xms500M 设置最小堆内存为 500MB
-Xmn200M 设置堆内存新生代内存大小为 200MB,Oracle推荐设置为整体内存的一半或者1/4
-XX:CompressedClassSpaceSize=100M 设置堆外类压缩空间大小为 100MB
-XX:MetaspaceSize=200M 设置堆外元数据空间大小为 200M
-XX:MaxMetaspaceSize=200M 设置堆外元数据空间最大值为 200M