一、jps
jps (JVM Process Status Tool)是其中的典型jvm工具。除了名字像 UNIX 的 ps 命令之外,它的功能也和 ps 命令类似:可以列出正在运行的虚拟机进程,并显示虚拟机执行主类(Main Class, main()函数所在的类)名称以及这些进程的本地虚拟机唯- ID (Local Virtual Machine Identifier, LVMID),虽然功能比较单一,但它是使用频率最高的 JDK 命令行工具。
jps -mlv
19337 mx-cms-web-1.0.0.jar --spring.profiles.active=test -Xms128m -Xmx512m -Xmn256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xverify:none -XX:+DisableExplicitGC -Djava.awt.headless=true
19562 mx-cms-online-1.0.0.jar --spring.profiles.active=test -Xms128m -Xmx512m -Xmn256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xverify:none -XX:+DisableExplicitGC -Djava.awt.headless=true
二、jstat
jstat(JVM Statistics Monitoring Tool)是用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程-虚拟机进程中的类装载、内存、垃圾收集、JIT 编译等运行数据,在没有 GU 图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。
6616表示进程id ,250表示250毫秒打印一次 ,20表示一共打印20次
jstat -gc 6616 250 20
jstat -gcutil 6616 250 20
S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
三、jinfo
jinfo (Configuration Info for Java)的作用是实时地查看和调整虚拟机各项参数。使用 jps 命令的-v 参数可以查看虚拟机启动时显式指定的参数列表,但如果想知道未被显式指定的参数的系统默认值,除了去找资料外,就只能使用 jinfo 的-flag 选项进行查询了。
jinfo pid
四、jmap
Jmap (Memory Map for Java)命令用于生成内存快照。如果不使用 jmap 命令,要想获取 Java 堆转储快照,还有一些比较“暴力”的手段:-XX:+HeapDumpOnOutOfMemoryError -XX:+HeapDumpPath=/usr/local/dump 参数,可以让虚拟机在 OOM 异常出现之后自动生成 dump 文件,用于系统复盘环节,和 jinfo 命令一样,jmap 有不少功能在 Windows 平台下都是受限的,除了生成 dump 文件的- dump 选项和用于查看每个类的实例、空间占用统计的-histo选项在所有操作系统都提供之外,其余选项都只能在Linux/Solaris 下使用。
(1)打印heap的概要信息,GC使用的算法,heap(堆)的配置及JVM堆内存的使用情况。
jmap -heap 4250
(2)打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量。
less分页查看:空格键向下翻页,b向上翻页,q退出。
jmap -histo:live 4250 | less
num #instances(实例) #bytes(字节大小) class name(类名)
(3)-dump生成 Java 堆转储快照。
jmap -dump:live,format=b,file=/usr/local/dump/a.bin 4250
五、jhat
Sun JDK 提供 jhat (JVM Heap Analysis Tool)命令常与 jmap 搭配使用,来分析 jmap 生成的堆 转储快照。jhat内置了一个微型的HTTP/HTML服务器,生成dump文件的分析结果后,可以在浏览器中查看。
jhat /usr/local/dump/a.bin
六、jstack
jstack (Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照(-般称为 threaddump 或者 javacore 文件)
线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的常见原因。线程出现停顿的时候通过 jstack 来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做些什么事情,或者等待着什么资源。
jstack[option] pid
参数如下
-l 长列表,打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表
-m 打印java和native c/c++框架的所有信息
tid指java Thread id。nid指native线程的id。prio是线程的优先级[0x00007fd4f8684000]是线程栈起始地址
dump 文件里 值得关注线程有:
死锁,Deadlock(重点关注)
等待资源,Waiting on condition(重点关注)
等待获取监视器,Waiting on monitor entry(重点关注)
阻塞,Blocked(重点关注)
执行中,Runnable
暂停,Suspended
对象等待中,Object.wait() 或 TIMED_WAITING
停止,Parked
jstack -l 4250
-F 当正常输出的请求不被响应时,强制输出线程堆栈。
六、Dump文件结构
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.77-b03 mixed mode):
"Attach Listener" #37 daemon prio=9 os_prio=0 tid=0x00007f87b42b7000 nid=0x680f waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Druid-ConnectionPool-Destory-331358539" #36 daemon prio=5 os_prio=0 tid=0x00007f87a4278800 nid=0x67e4 waiting on condition [0x00007f87b8dce000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at com.alibaba.druid.pool.DruidDataSource$DestroyConnectionThread.run(DruidDataSource.java:1724)
"Druid-ConnectionPool-Create-331358539" #35 daemon prio=5 os_prio=0 tid=0x00007f87a4022000 nid=0x67e3 waiting on condition [0x00007f87ce86a000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000b3804848> (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 com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:1629)
"Abandoned connection cleanup thread" #31 daemon prio=5 os_prio=0 tid=0x00007f87e0d91800 nid=0x672b in Object.wait() [0x00007f87cd2c2000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x00000000b422d1e8> (a java.lang.ref.ReferenceQueue$Lock)
at com.mysql.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:43)
"DestroyJavaVM" #30 prio=5 os_prio=0 tid=0x00007f87e0008800 nid=0x670b waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"http-nio-8081-AsyncTimeout" #28 daemon prio=5 os_prio=0 tid=0x00007f87e016e800 nid=0x6727 waiting on condition [0x00007f87b94cf000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at org.apache.coyote.AbstractProtocol$AsyncTimeout.run(AbstractProtocol.java:1211)
at java.lang.Thread.run(Thread.java:745)
"http-nio-8081-Acceptor-0" #27 daemon prio=5 os_prio=0 tid=0x00007f87e0166000 nid=0x6726 runnable [0x00007f87b95d0000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250)
- locked <0x00000000b410d480> (a java.lang.Object)
at org.apache.tomcat.util.net.NioEndpoint$Acceptor.run(NioEndpoint.java:455)
at java.lang.Thread.run(Thread.java:745)
"http-nio-8081-ClientPoller-0" #26 daemon prio=5 os_prio=0 tid=0x00007f87e0292800 nid=0x6725 runnable [0x00007f87b96d1000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
- locked <0x00000000b410d6c0> (a sun.nio.ch.Util$2)
- locked <0x00000000b410d6b0> (a java.util.Collections$UnmodifiableSet)
- locked <0x00000000b410d668> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:793)
at java.lang.Thread.run(Thread.java:745)
"http-nio-8081-exec-10" #25 daemon prio=5 os_prio=0 tid=0x00007f87e028c000 nid=0x6724 waiting on condition [0x00007f87b97d2000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000b410d898> (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 org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:103)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:31)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
其中每个空行用于分隔一个线程,每个线程的信息是以堆栈信息的方式展开,显示了目前正在调用的方法以及所在的代码行。每个线程信息的第一行是描述该线程的基本信息,例如:
"http-nio-8081-exec-10" #25 daemon prio=5 os_prio=0 tid=0x00007f87e028c000 nid=0x6724 waiting on condition [0x00007f87b97d2000]
每一个部分的含义如下:
“http-nio-8081-exec-10” 线程名称
#25 线程编号
daemon 线程的类型
prio=5 线程的优先级别
os_prio=0 系统级别的线程优先级
tid=0x00007f87e028c000 线程ID
nid=0x6724 native线程的id
waiting on condition [0x00007f87b97d2000] 线程当前的状态
线程当前的状态是我们主要关注的内容。
七、CPU占用过高的定位分析
1、查看进程pid
(1)
ps ux | grep prog_name
(2) 使用top命令查询进程pid
(3)通过进程 pid 查看线程信息
方法一:
ps -Lf $pid
-L Show threads, possibly with LWP and NLWP columns
-f does full-format listing.
[test1280@localhost ~]$ ps -Lf 11029
UID PID PPID LWP C NLWP STIME TTY STAT TIME CMD
test1280 11029 9374 11029 0 4 10:58 pts/0 Sl+ 0:00 ./main
test1280 11029 9374 11030 0 4 10:58 pts/0 Sl+ 0:00 ./main
test1280 11029 9374 11031 0 4 10:58 pts/0 Sl+ 0:00 ./main
test1280 11029 9374 11032 0 4 10:58 pts/0 Sl+ 0:00 ./main
11209是待观察的进程的PID。
输出中可见此进程包含4个线程,他们的PID都是11209,PPID都是9374,其中LWP即我们要找的线程ID。
我们注意到有一个线程的LWP同进程的PID一致,那个线程就是主线程。
方法二:
通过进程ID查到哪些线程占比最高
top -Hp $pid
在top中指定了进程PID,输出包含线程信息,通过PID字段可获知每个线程的PID(TID/LWP)。
将最高的线程PID转换为16进制
printf '0x%x\n' 线程PID
然后通过jstack查看具体线程信息
jstack 进程PID | grep 16进制线程PID -A 20
方法三:
ps -efL | grep prog_name
方法四:
ls /proc/pid/task
2、使用jps进一步定位:
3、定位到具体线程:
ps -mp 进程PID -o THREAD,tid,time
4、需要注意的是,在jsatck命令展示的结果中,线程id都转换成了十六进制形式。可以用如下命令查看转换结果将 TID 转换为16进制(或者使用计算器)
printf "%x\n" 线程ID
4、定位到具体代码:
jstack 进程ID | grep tid(16进制线程ID小写英文) -A60
可以将信息输出到文件
jstack 进程ID | grep tid(16进制线程ID小写英文) -A60 > a.txt
参考链接:
https://blog.youkuaiyun.com/zbajie001/article/details/80045710
https://www.cnblogs.com/shihaiming/p/11451583.html
https://www.jb51.net/article/186289.htm
https://blog.youkuaiyun.com/xiaofeivip_top/article/details/97679507
https://blog.youkuaiyun.com/sheldon178/article/details/79543671
https://blog.youkuaiyun.com/qq_32641659/article/details/88035588