Open BruceOuyang opened 2 years ago
command | remark |
---|---|
jps | 主要用来输出JVM中运行的进程状态信息,全称 Java Virtual Machine Process Status Tool |
jstack | 主要用来查看某个Java进程内的线程堆栈信息,使用率很高 |
jmap | 用来查看堆内存使用状况,一般结合jhat使用 |
jhat | 用来分析jmap导出的数据信息 |
jstat | JVM统计监测工具 |
hprof | 能够展现CPU使用率,统计堆内存使用情况 |
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
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线程并定位堆栈信息,一般步骤:
1)找到 pid
[root@localhost ~]# jps
79426 Jps
44639 dongsee.jar
这里我们用 dongsee.jar 来测试,它的 pid 为 44639
2)找到 pid 最占资源的线程id
[root@localhost ~]# top -Hp 44639
top - 18:07:01 up 9 days, 2:56, 3 users, load average: 0.03, 0.05, 0.05
Threads: 68 total, 0 running, 68 sleeping, 0 stopped, 0 zombie
%Cpu(s): 2.3 us, 0.9 sy, 0.0 ni, 96.3 id, 0.3 wa, 0.0 hi, 0.2 si, 0.0 st
KiB Mem : 8581928 total, 1076676 free, 7182236 used, 323016 buff/cache
KiB Swap: 4390908 total, 3593724 free, 797184 used. 1153924 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
44758 root 20 0 4894932 984064 5024 S 4.0 11.5 13:55.12 System Clock
44736 root 20 0 4894932 984064 5024 S 0.7 11.5 1:13.61 SimplePauseDete
44737 root 20 0 4894932 984064 5024 S 0.7 11.5 1:12.71 SimplePauseDete
44647 root 20 0 4894932 984064 5024 S 0.3 11.5 0:40.59 C2 CompilerThre
44738 root 20 0 4894932 984064 5024 S 0.3 11.5 1:14.60 SimplePauseDete
44741 root 20 0 4894932 984064 5024 S 0.3 11.5 0:02.90 QuartzScheduler
44639 root 20 0 4894932 984064 5024 S 0.0 11.5 0:00.02 java
44640 root 20 0 4894932 984064 5024 S 0.0 11.5 0:18.71 java
44641 root 20 0 4894932 984064 5024 S 0.0 11.5 0:00.93 java
输出的第一个就是最占资源的线程了,第一列及它的id,这里是 44758
3)将线程id转为十六进制
[root@localhost ~]# printf '%x\n' 44758
aed6
十六进制的 aed6 在堆栈中会有打印
4)抓取堆栈信息
[root@localhost ~]# jstack -l 44639 > jstack.44639
jstack.44639 这个文件名可以随意命名,用来存放堆栈信息
可根据线程状态统计排序
grep java.lang.Thread.State jstack.44639 | awk '{print $2$3$4$5}' | sort | uniq -c
5)从堆栈信息中过滤出线程id相关堆栈信息
[root@localhost ~]# cat jstack.44639 | grep aed6 -A 20
"System Clock" #61 daemon prio=5 os_prio=0 tid=0x00007fe5b4189000 nid=0xaed6 runnable [0x00007fe581ef6000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007b79bc288> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- None
"DestroyJavaVM" #60 prio=5 os_prio=0 tid=0x00007fe5e004b800 nid=0xae60 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
> `grep -A n` 展示关键字向下n行,`grep -C n` 展示关键字向下和向下各n行
需根据自己的情况具体分析
1、语法
jmap [option] pid
jmap [option] executable core
jmap [option] [server-id@]remote-hostname-or-ip
2、示例
jmap -heap pid
查看进程堆内存使用情况,包括使用的GC算法、堆配置参数和各代中堆内存使用情况
[root@localhost jvm]# jmap -heap 44639
Attaching to process ID 44639, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.292-b10
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则只统计活对象
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 的分析结果
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)
利用 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
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
jmap –histo:live $pid | sort-n -r -k2 | head-n 50
#内存
ps axo %mem,pid,euser,cmd | sort -nr | head -10
#CPU
ps -aeo pcpu,user,pid,cmd | sort -nr | head -10
grep "cpu " /proc/stat | awk -F ' ' '{total = $2 + $3 + $4 + $5} END {print "idle \t used\n" $5*100/total "% " $2*100/total "%"}'
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}';
推荐大家使用 show-busy-java-threads
脚本,该脚本可用于快速排查 Java 的 CPU 性能问题(top us 值过高),自动查出运行的 Java 进程中消耗 CPU 多的线程,并打印出其线程栈,从而确定导致性能问题的方法调用,该脚本已经用于阿里线上运维环境。链接地址:https://github.com/oldratlee/useful-scripts
# 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
for file in /proc/*/status ; do awk '/VmSwap|Name|^Pid/{printf $2 " " $3}END{ print ""}' $file; done | sort -k 3 -n -r | head -10
#显示最后一次或当前正在发生的垃圾收集的诱发原因
jstat -gccause $pid
#显示各个代的容量及使用情况
jstat -gccapacity $pid
#显示新生代容量及使用情况
jstat -gcnewcapacity $pid
#显示老年代容量
jstat -gcoldcapacity $pid
#显示垃圾收集信息(间隔1秒持续输出)
jstat -gcutil $pid 1000
# 快速杀死所有的 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
启动脚本 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 |