JVM性能调优监控工具使用详解

  1. 云栖社区>
  2. Java架构沉思录>
  3. 博客>
  4. 正文

JVM性能调优监控工具使用详解

技术小能手 2018-10-30 10:47:20 浏览3277
展开阅读全文

现实企业级Java开发中,有时候我们会碰到下面这些问题:

 ●  OutOfMemoryError,内存不足
 ●  内存泄露
 ●  线程死锁
 ●  锁争用(Lock Contention)
 ●  Java进程消耗CPU过高
 ●  ......

这些问题在日常开发中可能被很多人忽视(比如有的人遇到上面的问题只是重启服务器或者调大内存,而不会深究问题根源),但能够理解并解决这些问题是Java程序员进阶的必备要求。本文将对一些常用的JVM性能调优监控工具进行介绍,希望能起抛砖引玉之用。本文参考了网上很多资料,难以一一列举,在此对这些资料的作者表示感谢!关于JVM性能调优相关的资料,请参考文末。

A、 jps(Java Virtual Machine Process Status Tool)

jps主要用来输出JVM中运行的进程状态信息。语法格式如下:

27dba29a09ce53800e5534d9d0f498ad02ad69c1

如果不指定hostid就默认为当前主机或服务器。

命令行参数选项说明如下:

447286bcf5cfdf6e31bb001a080e40bf72497de4

比如下面:

58fe127ae897bd39d83b117d3ba596510e79d7a4

B、 jstack

jstack主要用来查看某个Java进程内的线程堆栈信息。语法格式如下:

8f622d7c3e20b54549b8405612b42b2039071436

命令行参数选项说明如下:

e82ac36af6147b082d1321d7e6f29bd100f86be3

jstack可以定位到线程堆栈,根据堆栈信息我们可以定位到具体代码,所以它在JVM性能调优中使用得非常多。下面我们来一个实例找出某个Java进程中最耗费CPU的Java线程并定位堆栈信息,用到的命令有ps、top、printf、jstack、grep。

第一步先找出Java进程ID,我部署在服务器上的Java应用名称为mrf-center:

59e822ae750435480fedcf238cb701c8b9f773da

得到进程ID为21711,第二步找出该进程内最耗费CPU的线程,可以使用ps -Lfp pid或者ps -mp pid -o THREAD, tid, time或者top -Hp pid,我这里用第三个,输出如下:

871cc28cba655130d09d70a100c902533726f1b6

TIME列就是各个Java线程耗费的CPU时间,CPU时间最长的是线程ID为21742的线程,用

79da423f2e839dd68cafe8abebbb147e2cc89cf9

得到21742的十六进制值为54ee,下面会用到。

OK,下一步终于轮到jstack上场了,它用来输出进程21711的堆栈信息,然后根据线程ID的十六进制值grep,如下:

e331964420cf7e6c0457dc6e43380dc800278735

可以看到CPU消耗在PollIntervalRetrySchedulerThread这个类的Object.wait(),我找了下我的代码,定位到下面的代码:

fe48ecf9a5c7dddda2cbd85e9a0b3906e394fc10

它是轮询任务的空闲等待代码,上面的sigLock.wait(timeUntilContinue)就对应了前面的Object.wait()。

C、 jmap(Memory Map)和jhat(Java Heap Analysis Tool)

jmap用来查看堆内存使用状况,一般结合jhat使用。

jmap语法格式如下:

c5d58ea495f959dd3454c1fe9e18f3951194fd3c

如果运行在64位JVM上,可能需要指定-J-d64命令选项参数。

db73a7f4803ccb448f6098e468b804cc344277ae

打印进程的类加载器和类加载器加载的持久代对象信息,输出:类加载器名称、对象是否存活(不可靠)、对象地址、父类加载器、已加载的类大小等信息,如下图:

be71d7890c24d7c3fc2546a017c4b41afccb1d56

使用jmap -heap pid查看进程堆内存使用情况,包括使用的GC算法、堆配置参数和各代中堆内存使用情况。比如下面的例子:

2549ecca9fdd6a70e2de4be45bef745aae22cf67

使用jmap -histo[:live] pid查看堆内存中的对象数目、大小统计直方图,如果带上live则只统计活对象,如下:

b4b9e813742a4d2608832234c5e3868a15e9dc48

class name是对象类型,说明如下:

5180cf5bb64d97b6adb18808f5c043c427c25785

还有一个很常用的情况是:用jmap把进程内存使用情况dump到文件中,再用jhat分析查看。jmap进行dump命令格式如下:

f1c5e8efe66dc706c78161880ac46796fe4c0a26

我一样地对上面进程ID为21711进行Dump:

a491314769fe1ea748c8889911dbca2e3fc449a8

dump出来的文件可以用MAT、VisualVM等工具查看,这里用jhat查看:

98ae18177de45629053b491e8f204199b17a4d46

注意如果Dump文件太大,可能需要加上-J-Xmx512m这种参数指定最大堆内存,即jhat -J-Xmx512m -port 9998 /tmp/dump.dat。然后就可以在浏览器中输入主机地址:9998查看了:

92f369d5ac20eee17cad65428844baa5a8223b53

上面红线框出来的部分大家可以自己去摸索下,最后一项支持OQL(对象查询语言)。

D、jstat(JVM统计监测工具)

语法格式如下:

b2b5ebe679121283a8bd91f030eb1938077b8693

vmid是Java虚拟机ID,在Linux/Unix系统上一般就是进程ID。interval是采样时间间隔。count是采样数目。比如下面输出的是GC信息,采样时间间隔为250ms,采样数为4:

4d59d33c9e0cd8ec4e660f7e77e63ec319d5028f

要明白上面各列的意义,先看JVM堆内存布局:

c6ee3aa1b0ab2cbfcb321b36d3f7d15508b928d4

可以看出:

803838a41f70443e306958ef5f06731dc959029c

现在来解释各列含义:

0976bddd6f5742d626151cab97b10738b0ab9e10

E、hprof(Heap/CPU Profiling Tool)

hprof能够展现CPU使用率,统计堆内存使用情况。

语法格式如下:

ecc540b92dcff1bfc655e9345c9efa7aadfc91c6

完整的命令选项如下:

5fa9aeb9b0b8d0c93bad463c4f89714ed8e3dca4

来几个官方指南上的实例。

CPU Usage Sampling Profiling(cpu=samples)的例子:

adb43d0a38d0015b44600007a2b9e5b7890a13bd

上面每隔20毫秒采样CPU消耗信息,堆栈深度为3,生成的profile文件名称是java.hprof.txt,在当前目录。

CPU Usage Times Profiling(cpu=times)的例子,它相对于CPU Usage Sampling Profile能够获得更加细粒度的CPU消耗信息,能够细到每个方法调用的开始和结束,它的实现使用了字节码注入技术(BCI):

0cef35d5591ab690b5937d8c902f24b2d5ff801f

Heap Allocation Profiling(heap=sites)的例子:

210458890d6031b171536902171a8ec382454b8f

Heap Dump(heap=dump)的例子,它比上面的Heap Allocation Profiling能生成更详细的Heap Dump信息:

4d7d0bc0407dbf455f6f732350c29e14cc175f47

虽然在JVM启动参数中加入-Xrunprof:heap=sites参数可以生成CPU/Heap Profile文件,但对JVM性能影响非常大,不建议在线上服务器环境使用。


原文发布时间为:2018-10-28

本文作者:优雅先生

本文来自云栖社区合作伙伴“Java架构沉思录”,了解相关信息可以关注“Java架构沉思录”。

网友评论

登录后评论
0/500
评论
技术小能手
+ 关注
所属团队号: Java架构沉思录