1. 性能问题发现
1.1 用户体验
a. RT : 超时
b. TPS 率:达不到预期值
c. 错误率:比预期值高
以上数据来源为压测工具端的统计监控
1.2 被测端监控告警
资源层:系统 load 值、内存使用率、磁盘使用率、网络带宽等超过阈值
业务层:连接数满,full GC频率过高等
2.性能问题定位流程
3. Java 类问题定位常用工具& 样例
Java类应用常见问题:
- OutOfMemoryError,内存不足
- 内存泄露
- 线程死锁
- 锁争用(Lock Contention)
- Java进程消耗CPU过高
- ……
3.1 问题定位工具
Jstat –gcutil [PID]
初步定位Java应用是否存在内存问题
查看JVM堆内存使用是否正常
使用MAT分析异常的内存dump(使用Jmap导出)
Jstack [PID]
初步定位Java应用是否存在线程问题
dump 文件里,值得关注的线程状态有:
死锁,Deadlock(重点关注) 执行中,Runnable 等待资源,Waiting on condition(重点关注) 等待获取监视器,Waiting on monitor entry(重点关注) 暂停,Suspended 对象等待中,Object.wait() 或 TIMED_WAITING 阻塞,Blocked(重点关注) 停止,Parked
查看热点线程的调用树,判断可疑的业务代码
Jstack结果可视化查看工具——jca工具
jmap -dump:live,format=b,file=dump.hprof [PID]
使用MAT分析dump文件
GCLOG
稳定性测试、长时间测试时使用,记录Java应用内存变化趋势
Java 性能综合分析器–JMC,jconsole
初步判断是Java应用存在性能问题时,使用Jmc定位详细问题原因,监控项目很全,可以远程登录监控,但有点消耗资源;
对于线上问题排查,还是建议使用jstat,jstack,jmap工具等,结合top、vmstat等快速排查和定位问题。
—-Linux环境下:
运行命令的用户必须和Java进程运行的用户一致。应用使用root用户启动,必须使用root用户运行Java工具。
—Windows环境下:
运行工具的用户与Java进程运行的用户一致。
Windows用户体系与Linux不同,最高权限用户是System,Java应用已服务方式运行时,所属用户都是System。一般无法直接切换成System用户。
使用PStool的PsExec来实现获取System权限
在命令行中,使用PsExec.exe –s [Java工具绝对路径 Java工具参数]
3.2 案例1:RT大+CPU高 问题定位
现象1
在XX 应用执行一个批量(2000个)逻辑服务迁移时,平台10min没有响应。
平台后端服务为tomcat
定位1:查看监控–CPU
查看服务器资源利用情况(也可以直接通过Windows性能监视器或Zabbix之类的监控平台)
查看Tomcat进程CPU资源利用情况,tomcat的CPU利用率达到>180%(linux可以使用top -H)
找出该进程内最耗费CPU的线程, top -Hp pid(top -Hp 19214),输出类似下图:
TIME列就是各个Java线程耗费的CPU时间,主要是19226和19227这两个线程消耗大量的CPU,而且时间累占用了一个小时左右,需要把线程号转换成十六进制,因为打印出来的堆栈信息里面的线程号是十六进制形式: printf “%x/n” 19226 —0x4bla, printf “%x/n” 19226 7 —0x4blb
定位2:查看 JVM Heap监控
查看进程的JVM Heap的情况(可以用jstat ,或者直接查看JVM的heap监控)
jstat -gcutil 19214 1000 #19214是进程号
看到这里,每次fullgc消耗的时间,可以用相隔的两行FGCT相减得到。JVM一直在FullGC,进程被挂起,无法响应业务。
初步可以确定CPU消耗主要是由于JVM的FullGC引起;
定位3:是否是fullGC 线程消耗CPU? —查看 进程堆栈 和线程情况
快速定位线程造成的问题用:jstack [PID(进程号)] | grep [PID(线程号十六进制)] -A 10
从现象2的找到的线程ID转成十六进制0x4bla,查找线程对应的业务。
看到线程 19226, 19227果然是GC线程,是FullGC活动在消耗CPU
使用jstack pid打印 进程堆栈信息
DB 连接线程处于 Timed_waiting状态(这个状态说明线程在等待唤醒,但设置了时限)
结合线程消耗CPU信息得出的结论是:Java程序连DB 连接相关的问题,导致GC线程一直占用CPU。
3. 案例2:RT 大+资源层无异常
现象:
并发登录时,通过监控发现在吞吐量达到100TPS时,打开首页和登出的响应时间突然快速升高(压测工具/平台)。
定位:
查看服务器的资源层监控,发现没有异常(CPU在35%,cpu load接近1,IO较小,load队列长度接近0)
由于操作系统层没有明显的异常,对应用层进程做分析
先看最外层应用tomcat,对tomcat抓了一个线程dump : jstack pid
查看dump内容,发现有一个BLOCKED状态的线程 "http-nio-8080-exec-43" #149 daemon prio=5 os_prio=0 tid=0x0000000010947000 nid=0x2898 waiting for monitor entry [0x00000000273ac000] java.lang.Thread.State: BLOCKED (on object monitor) at java.io.PrintStream.write(PrintStream.java:478) - waiting to lock <0x0000000080050520> (a java.io.PrintStream) … 锁lock< 0x0000000080050520>如下 "http-nio-8080-exec-18" #123 daemon prio=5 os_prio=0 tid=0x00000000121c9000 nid=0x4b40 runnable [0x0000000025dad000] java.lang.Thread.State: RUNNABLE at java.io.FileOutputStream.writeBytes(Native Method) at java.io.FileOutputStream.write(FileOutputStream.java:326) at java.io.BufferedOutputStream.write(BufferedOutputStream.java:122) - locked <0x0000000080050540> (a java.io.BufferedOutputStream)----------------------here at java.io.PrintStream.write(PrintStream.java:480) - locked <0x0000000080050520> (a java.io.PrintStream)----------------------here at org.apache.tomcat.util.log.SystemLogHandler.write(SystemLogHandler.java:177) at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)at java.io.PrintStream.write(PrintStream.java:480) - locked <0x0000000080050520> (a java.io.PrintStream) at org.apache.tomcat.util.log.SystemLogHandler.write(SystemLogHandler.java:177) at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221) at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291) at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:295) at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141) - locked <0x0000000081ada7f0> (a java.io.OutputStreamWriter) at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229) at org.apache.log4j.helpers.QuietWriter.flush(QuietWriter.java:57)----------------------日志相关 at org.apache.log4j.WriterAppender.subAppend(WriterAppender.java:315) at org.apache.log4j.WriterAppender.append(WriterAppender.java:159) at org.apache.log4j.AppenderSkeleton.doAppend(AppenderSkeleton.java:230) - locked <0x0000000081ad9bf8> (a org.apache.log4j.ConsoleAppender) at org.apache.log4j.helpers.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:65) at org.apache.log4j.Category.callAppenders(Category.java:203) - locked <0x0000000081ab9f28> (a org.apache.log4j.spi.RootLogger) at org.apache.log4j.Category.forcedLog(Category.java:388) at org.apache.log4j.Category.info(Category.java:663)
初步定位结论:从堆栈信息看,是写日志相关功能导致的线程阻塞,
解决方案
经过开发定位,日志之前用的是同步写,后续将同步写日志改成异步写
没有评论