Java性能问题监控排查,主要就是对Java线程的堆栈进行分析,这就用到了Thread Dump工具,就是打印出Java应用运行时的堆栈信息来定位执行步骤中出现的问题。
【记压力测试关闭其他因素:1、修改java应用的日志等级;2、必要时关闭日志输出;3、数据库(中间件)等产品的日志输出;4、数据库连接池配置(以测试CPU核心数*2为界点);5、索引优化,oracle可以用pl/sql查看执行计划,mysql可以看慢查询以及explain查看执行计划等】
一、记录几个工具及命令
1、jvisualvm,jdk内置的性能分析工具。位置在jdk的安装目录的bin下面,可视化工具。(比较推荐!)
2、jstack,jdk内置的堆栈跟踪工具。jstack <pid> > <file>
3、jstat,jdk内置的JVM检测统计工具。jstat -gcutil <pid> 2000 10
4、jmap,jdk内置的内存映射工具,查看内存分布情况。jmap -histo <pid>
5、top,查看cpu和内存占用情况,top -H -p <pid>
6、linux服务器系统信息查看https://blog.csdn.net/u011636440/article/details/78611838
二、使用步骤以及描述
1、jvisualvm
详情收藏http://www.51testing.com/html/38/n-3724238.html
根据自身实践情况,主要几个步骤:
no.1 在服务器上需要监控的应用中添加启动参数如下:(使用工具的jdk版本一定要和监控的应用的jdk版本一致)
JAVA_OPTS="-Dcom.sun.management.jmxremote.port=1099
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false"
启动完成之后先用telnet ip port 测试一下连通性
no.2 打开jvisualvm.exe 建立监控连接
no.3 远程连接到服务器,图中右击后选择JMX连接
no.4 成功连接并监控到Java进程的运行信息
no.5 获取主要信息进行分析,首先打开监视界面,会得到下面4块图形,我主要监控着上面两个,一个是CPU的使用情况和对内存的占用情况,在查看CPU的使用情况的时候,可以查看到平均1核的CPU使用率,对于压测来说,希望应用能压到90%的使用率,再分析性能。而对内存则主要看垃圾回收的频率以及是否内存溢出。
no.6 其次主要看了抽样器,顺便说一下,查看线程可以看线程的运行时间,又可能一个线程一直运行,而其他线程都一直等待,这样的情况可能某一处引起了锁等待,只要每个线程的时间片分配不平均的话,就需要查看具体线程dump信息了。
抽样器感觉很不错,可以监控到应用的哪个方法用了多长时间,可以针对性的去查找dump文件中次堆栈情况,比如
根据上面的图,可以看到上面的几个方法占用时间很久,浪费了很多的时间,将这几个方法记下来之后,去dump文件中查看这几个方法的堆栈位置。
no.7 生成线程dump
点击图中的线程dump,可以生成threaddump文件,也可以在x线程下面看运行时间比较久的线程,记住线程号,从下面的dump文件中查找时间过长的方法和线程:(根据RUNNABLE查找)
通过堆栈位置的查找可以定位到问题代码,并进行纠正。这个主要可以在代码级去避免不必要的影响性能的code。下面进行DUMP分析。
通过这样的方式,在此次的压力测试中发现有两处写文件时间占用久和数据库id生成等待比较久,定位问题之后,发现一个是日志打印(将日志级别调高),一个是应用中报文写文件的操作(之间去除这样的操作,因为不是必要的)。数据库生成序列的时候还有待优化。
2、jstack
不仅可以通过工具获取dump文件,在服务器上也可以通过命令实现,jstack是一种,我在jstack的时候出现Unable to open socket file: target process not responding or HotSpot VM not这样错误,网上解决无果,就用kill -3 Java进程号也可以生成,生成的文件就是在项目的/logs/xxx.out文件中,要翻到最下面,而且要多kill -3几次,还要等一会。
dump 文件里,需要关注的线程状态有:
- 死锁,Deadlock,死锁线程,一般指多个线程调用期间进入了相互资源占用,导致一直等待无法释放的情况。
- 执行中,Runnable ,一般指该线程正在执行状态中,该线程占用了资源,正在处理某个操作。
- 等待资源,Waiting on condition,线程正处于等待资源或等待某个条件的发生。
- 等待获取监视器,Waiting on monitor entry,Moniter 是Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者class的锁,每个对象都有,也仅有一个 Monitor。
- 暂停,Suspended
- 对象等待中,Object.wait() 或 TIMED_WAITING,线程正处于等待资源或等待某个条件的发生。
- 阻塞,Blocked(重点关注) ,线程正处于阻塞状态,指当前线程执行过程中,所需要的资源长时间等待却一直未能获取到,被容器的线程管理器标识为阻塞状态,可以理解为等待资源超时的线程。
- 停止,Parked
根据实际内容进行分析
"resin-22129" daemon prio=10 tid=0x00007fbe5c34e000 nid=0x4cb1 waiting on condition [0x00007fbe4ff7c000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:315)
at com.caucho.env.thread2.ResinThread2.park(ResinThread2.java:196)
at com.caucho.env.thread2.ResinThread2.runTasks(ResinThread2.java:147)
at com.caucho.env.thread2.ResinThread2.run(ResinThread2.java:118)
"Timer-20" daemon prio=10 tid=0x00007fe3a4bfb800 nid=0x1a31 in Object.wait() [0x00007fe3a077a000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000006f0620ff0> (a java.util.TaskQueue)
at java.util.TimerThread.mainLoop(Timer.java:552)
- locked <0x00000006f0620ff0> (a java.util.TaskQueue)
at java.util.TimerThread.run(Timer.java:505)
以上依次是:
"resin-22129"
线程名称:如果使用 java.lang.Thread 类生成一个线程的时候,线程名称为 Thread-(数字) 的形式,这里是resin生成的线程;daemon
线程类型:线程分为守护线程 (daemon) 和非守护线程 (non-daemon) 两种,通常都是守护线程;prio=10
线程优先级:默认为5,数字越大优先级越高;tid=0x00007fbe5c34e000
JVM线程的id:JVM内部线程的唯一标识,通过 java.lang.Thread.getId()获取,通常用自增的方式实现;nid=0x4cb1
系统线程id:对应的系统线程id(Native Thread ID),可以通过 top 命令进行查看,现场id是十六进制的形式;waiting on condition
系统线程状态:这里是系统的线程状态,具体的含义见下面 系统线程状态 部分;[0x00007fbe4ff7c000]
起始栈地址:线程堆栈调用的其实内存地址;java.lang.Thread.State: WAITING (parking)
JVM线程状态:这里标明了线程在代码级别的状态,详细的内容见下面的 JVM线程运行状态 部分。- 线程调用栈信息:下面就是当前线程调用的详细栈信息,用于代码的分析。堆栈信息应该从下向上解读,因为程序调用的顺序是从下向上的。
3、jstat待补充
4、jmap待补充
5、top
top命令可以查看当前系统运行的进程,以及cpu、内存等占用情况,主要是通过top找到Java进程
然后通过top -H -p4932 查看Java进程下线程的情况。
最后通过占用率比较高的线程号去dump文件中对应的堆栈信息,在定位问题。
三、linux常用命令整理
1、tail
主要就是tail -300f xx.log 查看实时日志,打印最新300条信息;
2、grep
管道,用法很多,经常用的就是ps -ef | grep -I 内容,grep 'xxx' /opt/.. > xx.log 将文件中包含xxx信息的内容打印到xx.log中
grep forest f.txt #文件查找
grep forest f.txt cpf.txt #多文件查找
grep 'log' /home/admin -r -n #目录下查找所有符合关键字的文件
cat f.txt | grep -i shopbase
grep 'shopbase' /home/admin -r -n --include *.{vm,java} #指定文件后缀
grep 'shopbase' /home/admin -r -n --exclude *.{vm,java} #反匹配
seq 10 | grep 5 -A 3 #上匹配
seq 10 | grep 5 -B 3 #下匹配
seq 10 | grep 5 -C 3 #上下匹配,平时用这个就妥了
cat f.txt | grep -c 'SHOPBASE'
3、find
常用作查找文件
find / -type f -size +1024M 在目录下查找文件大于1G的文件
find / -name xxx 在目录下查找名为xxx的文件
find . -type d(当前目录下的所有子目录)
find . -iname \*.txt(大小写都匹配)
4、df
df -h 查看磁盘占用情况
5、netstat -nat | grep -iw "port" | -wc -l
查看端口连接数,暂时发现性能越好,连接数越高,cpu占用率也越高
参考资源:
https://yq.aliyun.com/articles/652400?spm=a2c4e.11153940.blogcont69520.41.6a5cbec3geytas
https://yq.aliyun.com/articles/69520?utm_content=m_10360