深入浅出JVM之性能监控工具

一、系统性能监控

linux操作系统中有很多命令可以查看应用程序的性能情况,下面介绍一下这些常用的命令:

uptime:

可以查看的信息有系统时间,运行时间,连接数-其中每一个终端算一个连接,1,5,15分钟内的系统平均负载,也就是运行队列中的平均进程数。这是一个操作系统级的命令,可以查看当前操作系统的整体的状况如何。

top:常用的系统性能监控命令,可显示当前进程的cpu利用率和内存使用率,找到cpu利用率过高的进程,从而可以进一步分析问题。

vmstat:可以统计系统的CPU,内存,swap,io等情况,可以查看系统的繁忙状态。

pidstat:可以细致观察进程。该命令并非系统自带的工具,需要单独安装。在Centos下的安装命令是yum install sysstat,该命令可以用来监控CPU,监控IO以及监控内存。该命令的使用格式如下:

pidstat -p 指定进程 –u 监控CPU 每秒采样 一共3次

[root@node1 mnt]# pidstat -p 6377 -u 1 3 
Linux 3.10.0-693.el7.x86_64 (node1)     11/18/2019      _x86_64_        (2 CPU)

05:46:48 AM   UID       PID    %usr %system  %guest    %CPU   CPU  Command
05:46:49 AM   997      6377   95.00    0.00    0.00   95.00     1  java
05:46:50 AM   997      6377   97.03    0.00    0.00   97.03     1  java
05:46:51 AM   997      6377  100.00    0.00    0.00  100.00     1  java
Average:      997      6377   97.34    0.00    0.00   97.34     -  java

结果显示的是三条数据,每秒钟采样一次结果,-u的意思是只监控CPU,所以只显示了CPU使用的情况。

[root@node1 mnt]# pidstat -p 6377 -u 1 3 -t
Linux 3.10.0-693.el7.x86_64 (node1)     11/18/2019      _x86_64_        (2 CPU)

05:46:57 AM   UID      TGID       TID    %usr %system  %guest    %CPU   CPU  Command
05:46:58 AM   997      6377         -   97.03    0.99    0.00   98.02     1  java
05:46:58 AM   997         -      6377    0.00    0.00    0.00    0.00     1  |__java
05:46:58 AM   997         -      6410    0.00    0.00    0.00    0.00     1  |__java
05:46:58 AM   997         -      6411    0.99    0.00    0.00    0.99     0  |__java
05:46:58 AM   997         -      6412    3.96    0.00    0.00    3.96     0  |__java
...
05:46:58 AM   997         -      6556    0.00    0.00    0.00    0.00     1  |__java
05:46:58 AM   997         -      6557    0.00    0.00    0.00    0.00     1  |__java
...
05:46:58 AM   997         -      6587   92.08    0.00    0.00   92.08     0  |__java

在监控进程的命令后面加上参数-t,我们可以看到线程的明细信息,可以看到线程ID为6587的cpu使用率比较高,后面的CPU一列指的是使用的哪一个CPU,本利中一共有两个CPU,该线程运行在0号CPU上。

有时候我们不只要看CPU的使用情况,我们还需要看IO的占用情况,以查看系统的瓶颈是否在IO上,将-u替换成-t就可以查看IO的使用情况,下面是使用-d查看IO情况的例子:

[root@node1 mnt]# pidstat -p 6377 1 3 -d -t
Linux 3.10.0-693.el7.x86_64 (node1)     11/18/2019      _x86_64_        (2 CPU)
05:52:57 AM   UID      TGID       TID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
05:52:58 AM   997      6377         -      0.00      8.00      0.00  java
05:52:58 AM   997         -      6377      0.00      0.00      0.00  |__java
...
05:52:58 AM   997         -      6585      0.00      0.00      0.00  |__java
05:52:58 AM   997         -      6587      0.00      8.00      0.00  |__java
05:52:58 AM   997         -      6588      0.00      0.00      0.00  |__java
...

可以看到线程6587在进行写操作,速度是8kb每秒,与上面监控到的CPU利用率高的线程是一致的,后续可以进一步查看该线程,看该线程在进行何种操作。

扫描二维码关注公众号,回复: 10395054 查看本文章

2.java自带的工具

在%JAVA_HOME%/bin目录下有一些以exe为结尾的可执行文件,如jps.exe,这些是java自带的监控工具,其具体的实现是在tools.jar里面,这些可执行文件主要封装了一下入口。

jps

列出java进程,类似于ps命令,参数-q可以指定要输出的进程ID,不输出类的短名称,参数-m可以用于输出传递给main函数的参数,参数-l可以输出main函数的完整路径,-v则可以显示传递给jvm的参数。jps后面跟不同的参数会显示不同的信息:

[root@node1 ~]# su hdfs
[hdfs@node1 root]$ jps
42393 Jps
7214 NameNode
7217 SecondaryNameNode
7220 DataNode
[hdfs@node1 root]$ jps -m
7214 NameNode
42412 Jps -m
7217 SecondaryNameNode
7220 DataNode
[hdfs@node1 root]$ jps -m -l
7214 org.apache.hadoop.hdfs.server.namenode.NameNode
7217 org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode
7220 org.apache.hadoop.hdfs.server.datanode.DataNode
42434 sun.tools.jps.Jps -m -l
[hdfs@node1 root]$ jps -m -l -v
42517 sun.tools.jps.Jps -m -l -v -Dapplication.home=/usr/java/jdk1.7.0_80 -Xms8m
...

jps看到的信息是和ps查看的信息是一致的,jps可以只查看java进程,比ps要更专注一些,对于查找java程序的问题更方便一些。

jinfo

可以用来查看正在运行的java应用程序的扩展参数,并且支持在运行时修改部分参数。

-flag <ParName>:打印指定jvm的参数值

-flag [+|-]<ParName>:设置指定jvm参数的布尔值

-flag <ParName>=<value>:设置指定jvm参数的值:

/*查看新生代对象晋升到老年代对象的最大年龄,该参数显示的对象被复制的次数*/
[hdfs@node1 root]$ jinfo -flag MaxTenuringThreshold 7214
-XX:MaxTenuringThreshold=6
/*显示是否打印GC详细信息*/
[hdfs@node1 root]$ jinfo -flag PrintGCDetails 7214
-XX:-PrintGCDetails
/*修改运行时参数,设置可以打印GC详细信息*/
[hdfs@node1 root]$ jinfo -flag +PrintGCDetails  7214
[hdfs@node1 root]$ jinfo -flag PrintGCDetails 7214
-XX:+PrintGCDetails

jmap

生成java应用程序的堆快照和对象的统计信息。

jmap -histo 7214 > /tmp/class_info.txt

[hdfs@node1 tmp]$ jmap -histo 7214 > /tmp/class_info.txt

 num     #instances         #bytes  class name
----------------------------------------------
   1:         92532       12252192  <constMethodKlass>
   2:         92532       11854816  <methodKlass>
   3:          7112        9229816  <constantPoolKlass>
   ...
   3646:             1             16  java.lang.reflect.Proxy$KeyFactory
3647:             1             16  sun.reflect.GeneratedMethodAccessor197
Total        509627       65084496

类的统计信息是按实例数进行排序的,排在第一位的实例有92532个,类的名称是constMethodKlass,大小有12m左右,通过该统计信息可以初步判断应用程序中是否有异常的类。

Dump堆信息

可以将内存中的堆信息保存到一个文件中,然后利用分析工具可以进行离线分析。

jmap -dump:format=b,file=/tmp/dump_info.hprof 7214

jstack

打印线程信息,-l打印锁信息,-m打印java和native的帧信息,有时候jstack会没有响应,可以使用-F强制dump线程信息。

[hdfs@node1 tmp]$ jstack 7214 >> stack_info.txt
....
"main" prio=10 tid=0x00007f2158019800 nid=0x1d22 in Object.wait() [0x00007f215ef64000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000000f8e27c68> (a org.apache.hadoop.ipc.ProtobufRpcEngine$Server)
	at java.lang.Object.wait(Object.java:503)
	at org.apache.hadoop.ipc.Server.join(Server.java:2705)
	- locked <0x00000000f8e27c68> (a org.apache.hadoop.ipc.ProtobufRpcEngine$Server)
	at org.apache.hadoop.hdfs.server.namenode.NameNodeRpcServer.join(NameNodeRpcServer.java:458)
	at org.apache.hadoop.hdfs.server.namenode.NameNode.join(NameNode.java:890)
	at org.apache.hadoop.hdfs.server.namenode.NameNode.main(NameNode.java:1617)
	....

上面显示了main函数所在的线程的信息,其中tid是在jvm中的id,nid是在操作系统总的id。使用命令top -H -p 7214可以查看进程7214的线程信息,其中pid=7458就是main函数对应的线程信息。可以根据这个找到异常的进程所对应的函数,进而查找定位出问题的地方。

JConsole

图形化的监控工具,可以用来查看java应用程序的运行概况,监控堆信息,永久区的使用情况,类加载情况以及线程的信息等。jconsole的启动文件是jconsole.exe

Visual VM

Visual VM是一个功能强大的多合一故障诊断和性能监控的可视化工具,基本上jconsole能实现的功能visual vm都有,还能显示更详细的信息。Visual VM的启动文件是jvisualvm.exe。visual VM可以dump堆的信息,可以比较方便的分析堆内容。

死锁

假设线程A持有资源S1,请求资源S2,线程B持有资源S2,请求资源S1,S1和S2资源都是线程独占的,也就是一旦这个资源被持有,其他线程就得等待,等另外一个线程释放该资源后才可以被其他线程持有。这种情况下,线程A等待线程B释放资源,同时线程B也在等待线程A释放资源,如果没有外来力量的干预,就会一直等待下去,这种情况就是死锁。

通过JStack可以找到线程的死锁,通过每个线程锁定和等待的对象ID就可以找到对应的死锁线程ID.

发布了36 篇原创文章 · 获赞 2 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/xjjdlut/article/details/105241837