资源(CPU,memory,thread)相关快速诊断.md - liuxiang/liuxiang.github.io GitHub Wiki

一.java进程信息

# 查看java进程号(pid) : 
> jps
> pidof java	  #java进程 
> pgrep java		#java进程(推荐)
> pgrep -n java #最近一个java进程

# 机器安装多java进程服务,可参考如下匹配
ps -ef |grep "holmes-web"|grep -v "grep"|awk '{print $2}'

# 信息查看: 
> jinfo $pid

二.CPU (一键诊断: 进程下最消耗CPU的线程栈详情)

  • 单行命令(压缩) 仅适合一台机器仅一个java进程情况
# 获取进程下最消耗CPU的线程栈详情
process=`pgrep java` && echo $process; \
	top -c -b -n 1 -Hp $process | sed -n '1,11p'; \
	top1pid=`top -c -b -n 1 -Hp $process| sed -n '8,8p'|awk '{print $1}'` && echo top1pid: $top1pid; \
	thread=`printf '%x' $top1pid`; echo thread-nid: $thread; \
	[ $thread != 0 ] && jstack $process|grep 0x$thread -A20
# main函数栈
process=`pgrep -n java` && echo $process;	jstack $process|grep \"main\" -A20
  • 分解
# 1.定位java进程(也可以用pgrep java)
process=`pidof java`  
echo $process "(如遇多个java进程,自行赋值)"

# 2.定位top1线程pid(第八行:sed -n '8,8p')
top1pid=`top -c -b -n 1 -Hp $process| sed -n '8,8p' | cut -d ' ' -f1`

# 3.线程pid转十六进制
thread=`printf '%x' $top1pid`; echo $thread; 

# 4.打印top1线程栈[显示行数: 15 (可修改)]
[ -n $thread ] && jstack $process | grep 0x$thread -A15

1.[ -n $thread ] 非空, [ $thread != 0 ] 非0

2.||则与&&相反。如果||左边的命令(命令1)未执行成功,那么就执行||右边的命令(命令2);或者换句话说,“如果这个命令执行失败了||那么就执行这个命令

3.一条命令需要独占一个物理行,如果需要将多条命令放在同一行,命令之间使用命令分隔符(;)分隔。执行的效果等同于多个独立的命令单独执行的效果. 分隔符(;)也标记着一串命令的结束.

  • 其它提取线程ID的几种方式
echo `head + awk`
topT=`top -c -b -n 1 -Hp $process |grep admin|head -n 1| awk '{print $1}'`

echo `cut -d`(空格分割,取数组位1)
topT=`top -c -b -n 1 -Hp $process |grep admin|head -n 1| cut -d ' ' -f1`

echo `tail -n +8` (第8行开始到最后)
topT=`top -c -b -n 1 -Hp $process|tail -n +8 | head -n 1 | cut -d ' ' -f1`

echo `sed -n '8,8p' ` (第8行开始到第8行)
topT=`top -c -b -n 1 -Hp $process|sed -n '8,8p' | cut -d ' ' -f1`

参考 关于shell中,如何得到top命令显示的进程号? https://bbs.csdn.net/topics/350053680


三.内存

  • GC情况:jstat -gc <pid> 1000 5
  • 堆使用情况:jmap -heap <pid>
  • jvm配置情况: jinfo -flags <pid>
pgrep -n java | xargs jmap -heap    (高版本jdk已经没有了-heap)
pgrep -n java | xargs jinfo -flags

# 可能出现的问题:
1.VMVersionMismatchException
> 启动应用版本与诊断jdk版本不一致

2.Caused by: sun.jvm.hotspot.debugger.DebuggerException: Can't attach to the process. Could be caused by an incorrect pid or lack of privileges
> 权限问题 or jdk8部分版本问题(>jdk9诊断无问题) 
注:可以用高版本jdk14诊断低版本jdk8启动的应用.

3.Cannot connect to core dump or remote debug server. Use jhsdb jmap instead
> 使用jhsdb (但依然会出现以上其它问题)
jhsdb jmap --heap --pid 5784
jhsdb jinfo --flags --pid 5784

Mac JDK8 jmap can't attach to the process.

  • 对象内存分布:jmap -histo:live $pid
pgrep -n java | xargs jmap -histo | head -n 20
pgrep -n java | xargs jmap -histo  | grep cn.fraudmetrix | head -n 20
pgrep -n java | xargs jmap -histo:live | grep cn.fraudmetrix | head -n 20
  • heap dump (jmap -dump:format=b,file=dumpfileName.dump $pid)
pgrep java | xargs jmap -dump:format=b,file=dumpfileName.dump 

查看JVM使用的什么垃圾收集器

jinfo -flags <pid>

-XX:+Use*** 为当前使用垃圾回收器

机器内存

消耗内存的top进程: ps aux|head -1; ps aux|grep -v PID|sort -rn -k +4|head (或: ps aux | sort -k4nr |head -n 10)

Linux下如何查看哪些进程占用的CPU内存资源最多 https://www.cnblogs.com/sparkbj/p/6148817.html

linux查看进程占用cpu、内存、io信息 http://blog.51cto.com/liuqun/2049656


四.线程堆栈(jstack)

# 打印堆栈(相隔3s执行一次,执行5次:可获得持续3s~15s均未执行结束的线程.排除守护线程外基本可判断为高嫌疑线程)
jstack [PID] >> jstack.log

# 显示系统java线程总数
ps -eLf | grep java -c
  • [分组统计]堆栈中各线程状态
process=`pgrep java`;jstack $process > jstack.log;
cat jstack.log | grep 'java.lang.Thread.State' | awk '{print $2" "$3" "$4" "$5}' | sort | uniq -c
# 效果
  34 RUNNABLE   
   4 TIMED_WAITING (on object monitor)
  11 TIMED_WAITING (parking)  
   7 TIMED_WAITING (sleeping)  
   2 WAITING (on object monitor)
  27 WAITING (parking)  
  • [分组统计]各线程组
process=`pgrep java`;jstack $process > jstack.log;
cat jstack.log | grep 'tid=' | awk '{print $1}' | awk -F '-' '{print $1"-"$2"-"$3}' | uniq -c | sort -rnk 1 | head -10

cat jstack.log | grep 'tid=' | awk '{print $1}' | awk -F '-' '{print $1"-x-"$3}' | sort -rnk 1 | uniq -c |sort -rnk 1| head -10

# 示例效果
  14 "http-nio-8010
   6 "New--
   4 "RMI--
   4 "GC--
   3 "JDWP--
   2 "metrics-meter-tick
   2 "C2--
   1 "watchdog_sync_data_liuxiangs-MacBook-Air.local
   1 "watchdog_sync_data_liuxiangs-MacBook-Air.local
   1 "statDailyScheduler-1"-
"pool-15780-thread-1" #15980 prio=5 os_prio=0 tid=0x00007f5830d05000 nid=0x6e9f waiting on condition [0x00007f53c6ef2000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000005d09e7d40> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
	at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
	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)

tid: java内的线程id
nid: 操作系统级别线程的线程id
prio: java内定义的线程的优先级
os_prio: 操作系统级别的优先级

五.网络(连接情况)

# 连接数
netstat -na | wc -l

# 有效连接数
netstat -nat | grep ESTABLISHED | wc -l 

# 对各种状态的连接数分组统计结果
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
SYN_SENT 2
LAST_ACK 1
CLOSE_WAIT 4
TIME_WAIT 256
ESTABLISHED 96
  • 获取当前ip
inet_ip=`/sbin/ifconfig|grep inet|grep -v inet6|grep -v 127.0.0.1|awk '{if(substr($2,1,5)=="addr:"){print substr($2,6)} else{print $2}}'|head -n 1`
echo $inet_ip

或
inet_ip=`/sbin/ifconfig|grep inet|grep -v inet6|grep -v 127.0.0.1 | cut -d ' ' -f2`
echo $inet_ip

或
IP=`ip a|grep -w 'inet'|grep 'global'|sed 's/^.*inet //g'|sed 's/\/[0-9][0-9].*$//g'`
echo $IP
  • 查看连接某服务端口最多的的IP地址
netstat -nat | grep "ESTABLISHED" | grep $inet_ip | awk '{print $5}' | awk -F: '{print $1}' | sort | uniq -c | sort -nr | head -20 
  14 192.168.6.28.80
  12 192.168.6.70.3306
  10 10.57.17.28.3306
   4 192.168.6.56.2181
   4 192.168.6.55.9092
   3 192.168.8.126.993
   2 192.168.6.57.2181
   2 192.168.6.56.9092
   2 151.101.72.133.443
   2 10.57.22.129.8080
   1 18.204.186.74.443
  • curl获取http各阶段的响应时间
curl -o ~ -s -w %{time_namelookup}::%{time_connect}::%{time_starttransfer}::%{time_total}::%{speed_download}"\n" "baidu.com"

带宽情况: sar -n DEV 2

[tdops@turing-d-016013 ~]$ sar -n DEV 2
Linux 3.10.0-693.11.6.el7.x86_64 (turing-d-016013.te.td) 	2021年04月16日 	_x86_64_	(8 CPU)

20时35分26秒     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s
20时35分28秒      eth0     53.00     28.00      4.88     19.13      0.00      0.00      0.00
20时35分28秒        lo   7360.50   7360.50   3119.00   3119.00      0.00      0.00      0.00
20时35分28秒   docker0      0.00      0.00      0.00      0.00      0.00      0.00      0.00

20时35分28秒     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s
20时35分30秒      eth0     44.00     20.50      4.06      2.38      0.00      0.00      0.00
20时35分30秒        lo   7384.00   7384.00   3124.16   3124.16      0.00      0.00      0.00
20时35分30秒   docker0      0.00      0.00      0.00      0.00      0.00      0.00      0.00
IFACE: LAN接口
rxpck/s: 每秒钟接收的数据包
txpck/s: 每秒钟发送的数据包

rxbyt/s: 每秒钟接收的字节数
txbyt/s: 每秒钟发送的字节数

rxcmp/s: 每秒钟接收的压缩数据包
txcmp/s: 每秒钟发送的压缩数据包

rxmcst/s: 每秒钟接收的多播数据包

六.工具&配置

tomcat jvm配置

  • 服务端配置远程调试
# 服务端配置远程调试
- Jdk1.7之前:
CATALINA_OPTS="$CATALINA_OPTS -server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5888"
- jdk1.7之后:
CATALINA_OPTS="$CATALINA_OPTS -server -agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n"

jconsole,VisualVM,JMC 监控配置(rmi)

# jconsole,jvisualvm,JMC 监控配置(rmi)
# -Dcom.sun.management.jmxremote.authenticate=false 为 JMC 关闭飞行模式
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=8999 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.managementote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=***.***.***.***"# set tomcat jmx
inet_ip=`/sbin/ifconfig|grep inet|grep -v inet6|grep -v 127.0.0.1|awk '{if(substr($2,1,5)=="addr:"){print substr($2,6)} else{print $2}}'|head -n 1`
CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=$inet_ip"

VisualVM使用jstatd远程监控jvm的多个服务

  • 配置安全策略
在jdk的bin目录(which java)下创建文件jstatd.all.policy
写入下面的安全配置

grant codebase "file:${java.home}/../lib/tools.jar" {
  permission java.security.AllPermission;
};
或
grant codebase "file:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.191.b12-1.el7_6.x86_64/lib/tools.jar" {
   permission java.security.AllPermission;
};

此处写绝对路径,主要是防止路径错误问题,排查问题,应该写成相对路径。
  • 生成 jstatd.all.policy
cd `which java` # 为参考
sudo tee jstatd.all.policy <<-'EOF'
grant codebase "file:${java.home}/../lib/tools.jar" {
  permission java.security.AllPermission;
};
EOF
  • 启动jstatd
./jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=106.15.95.37 &

或
inet_ip=`/sbin/ifconfig|grep inet|grep -v inet6|grep -v 127.0.0.1|awk '{if(substr($2,1,5)=="addr:"){print substr($2,6)} else{print $2}}'|head -n 1`
./jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=$inet_ip &
  • 查看jstatd是否启动成功
[root@sunpyServer bin]# jps -l       
3040 sun.tools.jstatd.Jstatd
3094 sun.tools.jps.Jps

其它

  • 日常GC日志以文件的方式输出到服务器(可用于回溯)
# 配置tomcat,日常GC日志. 可记录下服务器历史的GC情况
# 注意:tomcat启动后如果webapps下未自动创建sys目录,请手动创建 */webapps>mkdir sys
# 配置路径到webapps下,方便日志文件的下载和直接定位:
# 访问:
# http://121.40.87.121:8080/sys/gc.log
# http://121.40.84.8:8080/sys/gc.log
# 可视化工具:GCViewer可以使用网络地址直接查看
CATALINA_OPTS="$CATALINA_OPTS -Xloggc:webapps/sys/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps"

示例:

# YoungGC+OldGC分配失败
GC (Allocation Failure) [PSYoungGen: 22716K->680K(33280K)] 22716K->7810K(110080K), 0.0929256 secs] [Times: user=0.13 sys=0.00, real=0.09 secs] 

# OOM
[Full GC (Allocation Failure) [PSYoungGen: 480K->0K(2560K)] [ParOldGen: 249K->680K(7168K)] 729K->680K(9728K), [Metaspace: 3259K->3259K(1056768K)], 0.0076754 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

格式解读: https://www.cnblogs.com/A-Dun/p/13790719.html

  • jprofile服务器端配置
# jprofile服务器端配置
CATALINA_OPTS="$CATALINA_OPTS -agentlib:jprofilerti=port=8849,nowait -Xbootclasspath/a:/usr/local/jprofile/jprofiler9/bin/agent.jar"
  • jvm shut down时,输出dump文件
# jvm shut down时,输出dump文件
CATALINA_OPTS="$CATALINA_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/apache-tomcat-7.0.59/logs/heap.dump"

JDK工具箱 / 可视化工具集

jvm内置(jconsole,VisualVM,JMC & JFR)

  • JMC & JFR
# 1.定位目标java进程
pid=`pgrep java`;  ps -ef |grep "holmes-web"|grep -v "grep"|awk '{print $2}'
# pid=`ps -ef |grep "holmes-web"|grep -v "grep"|awk '{print $2}'`; # 机器中多java进程情况参考

# 2.先解锁技能
jcmd $pid VM.unlock_commercial_features 

# 3.采集(其中,delay参数表示profile延迟启动时间,duration表示持续采集时间,这里设置为2分钟)
jcmd $pid JFR.start name=myrec delay=20s duration=2m filename=/tmp/$pid.jfr

#settings定制采集配置,如tongdun.jfc.它默认有一个名为profile的配置,如果不想采集异常信息,也可以直接用它。
# jcmd $pid JFR.start name=myrec settings=tongdun delay=20s duration=2m filename=/tmp/$pid.jfr

# 4.移除: 采集数据生成后请执行下列命令移除这个采集
jcmd $pid JFR.stop name=myrec 

# 5.分析
这样就好了,等待2分钟+20秒,/tmp/pid.jfr文件就生成好了,这个文件直接导入JMC工具即可.idea-profiler还可分析火焰图.

settings表示使用哪种采集配置,这里用的就是第二步中放入的tongdun.jfc配置,它默认有一个名为profile的配置,如果不想采集异常信息,也可以直接用它。 查看工具: $JAVA_HOME/bin/jmc 介绍: https://www.oracle.com/java/technologies/javase/jmc-relnotes.html 单独安装:

$ tar zxf jmc-<version>_osx-x64.tar.gz
$ open ./jmc-<version>_osx-x64/JDK\ Mission\ Control.app

https://jdk.java.net/jmc/8/
https://www.oracle.com/java/technologies/javase/jmc8-install.html

三方(GCViewer,JProfiler)

  • GCViewer
  • 输出gc.log
CATALINA_OPTS="$CATALINA_OPTS -Xloggc:/usr/local/apache-tomcat-7.0.59/webapps/sys/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
或(启动处)
CATALINA_OPTS="$CATALINA_OPTS -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps"```bash
- 访问 `open:open-file` 或 gc.log放置为静态资源:http://localhost:8080/gc.log

- JProfiler
> ...

- spring-boot-admin
https://github.com/codecentric/spring-boot-admin
![](https://github.com/codecentric/spring-boot-admin/raw/master/images/screenshot-details.png)
  • dump分析工具: jvisualvm / IBM HeapAnalyzer / jmc / Idea-Profiler / jprofiler
# IBM HeapAnalyzer(ha456.jar) ★★★
生成dump: jmap -dump:format=b,file=dumpfileName.dump $pid
分析dump: 打开*.dump文件

# jvisualvm(可装插件Visual GC)
生成dump: 抽样器-内存-堆Dump (heapdump-***.hprof)
分析dump: 文件-载入(下载服务器上dump:heapdump-***.hprof)

# jmc
生成dump: jmap -dump:format=b,file=dumpfileName.hprof $pid
分析dump: 拖拽*.hprof文件到面板即可

# Idea - Profiler分析工具
Open Snapshot: 选择分析目标文件(*.hprof or *.jfr)

# jprofiler(试用/收费)
  • 远程下载dump
scp [email protected]:/home/admin/forseti-gateway/deploy/tomcat/temp/heapdump-1523203218367.hprof ~

jvm线上诊断工具

  • greys > arthas > jvm-sandbox

    ![image-20210311171911477](../../../Library/Application Support/typora-user-images/image-20210311171911477.png)

  • jvm-tools

  • vjtools

⚠️ **GitHub.com Fallback** ⚠️