现实企业级 Java 应用开发, 维护中, 有时候我们会碰到下面这些问题:
OutOfMemoryError, 内存不足
内存泄露
线程死锁
锁争用(Lock Contention)
Java 进程消耗 CPU 过高
......
这些问题在日常开发, 维护中可能被很多人忽视(比如有的人遇到上面的问题只是重启服务器或者调大内存, 而不会深究问题根源), 但能够理解并解决这些问题是 Java 程序员进阶的必备要求. 本文将对一些常用的 JVM 性能调优监控工具进行介绍, 希望能起抛砖引玉之用.
而且这些监控, 调优工具的使用, 无论你是运维, 开发, 测试, 都是必须掌握的.
A, jps(Java Virtual Machine Process Status Tool)
jps 主要用来输出 JVM 中运行的进程状态信息. 语法格式如下:
如果不指定 hostid 就默认为当前主机或服务器.
命令行参数选项说明如下:
比如下面:
B, jstack
jstack 主要用来查看某个 Java 进程内的线程堆栈信息. 语法格式如下:
命令行参数选项说明如下:
jstack 可以定位到线程堆栈, 根据堆栈信息我们可以定位到具体代码, 所以它在 JVM 性能调优中使用得非常多. 下面我们来一个实例找出某个 Java 进程中最耗费 CPU 的 Java 线程并定位堆栈信息, 用到的命令有 ps,top,printf,jstack,grep.
第一步先找出 Java 进程 ID, 我部署在服务器上的 Java 应用名称为 mrf-center:
得到进程 ID 为 21711, 第二步找出该进程内最耗费 CPU 的线程, 可以使用 ps -Lfp pid 或者 ps -mp pid -o THREAD, tid, time 或者 top -Hp pid, 我这里用第三个, 输出如下:
TIME 列就是各个 Java 线程耗费的 CPU 时间, CPU 时间最长的是线程 ID 为 21742 的线程, 用
得到 21742 的十六进制值为 54ee, 下面会用到.
OK, 下一步终于轮到 jstack 上场了, 它用来输出进程 21711 的堆栈信息, 然后根据线程 ID 的十六进制值 grep, 如下:
可以看到 CPU 消耗在 PollIntervalRetrySchedulerThread 这个类的 Object.wait(), 我找了下我的代码, 定位到下面的代码:
它是轮询任务的空闲等待代码, 上面的 sigLock.wait(timeUntilContinue)就对应了前面的 Object.wait().
C, jmap(Memory Map)和 jhat(Java Heap Analysis Tool)
jmap 用来查看堆内存使用状况, 一般结合 jhat 使用.
jmap 语法格式如下:
如果运行在 64 位 JVM 上, 可能需要指定 - J-d64 命令选项参数.
打印进程的类加载器和类加载器加载的持久代对象信息, 输出: 类加载器名称, 对象是否存活(不可靠), 对象地址, 父类加载器, 已加载的类大小等信息, 如下图:
使用 jmap -heap pid 查看进程堆内存使用情况, 包括使用的 GC 算法, 堆配置参数和各代中堆内存使用情况. 比如下面的例子:
使用 jmap -histo[:live] pid 查看堆内存中的对象数目, 大小统计直方图, 如果带上 live 则只统计活对象, 如下:
class name 是对象类型, 说明如下:
还有一个很常用的情况是: 用 jmap 把进程内存使用情况 dump 到文件中, 再用 jhat 分析查看. jmap 进行 dump 命令格式如下:
我一样地对上面进程 ID 为 21711 进行 Dump:
dump 出来的文件可以用 MAT,VisualVM 等工具查看, 这里用 jhat 查看:
注意如果 Dump 文件太大, 可能需要加上 - J-Xmx512m 这种参数指定最大堆内存, 即 jhat -J-Xmx512m -port 9998 /tmp/dump.dat. 然后就可以在浏览器中输入主机地址: 9998 查看了:
上面红线框出来的部分大家可以自己去摸索下, 最后一项支持 OQL(对象查询语言).
D,jstat(JVM 统计监测工具)
语法格式如下:
vmid 是 Java 虚拟机 ID, 在 Linux/Unix 系统上一般就是进程 ID.interval 是采样时间间隔. count 是采样数目. 比如下面输出的是 GC 信息, 采样时间间隔为 250ms, 采样数为 4:
要明白上面各列的意义, 先看 JVM 堆内存布局:
可以看出:
现在来解释各列含义:
E,hprof(Heap/CPU Profiling Tool)
hprof 能够展现 CPU 使用率, 统计堆内存使用情况.
语法格式如下:
完整的命令选项如下:
来几个官方指南上的实例.
CPU Usage Sampling Profiling(CPU=samples)的例子:
上面每隔 20 毫秒采样 CPU 消耗信息, 堆栈深度为 3, 生成的 profile 文件名称是 java.hprof.txt, 在当前目录.
CPU Usage Times Profiling(CPU=times)的例子, 它相对于 CPU Usage Sampling Profile 能够获得更加细粒度的 CPU 消耗信息, 能够细到每个方法调用的开始和结束, 它的实现使用了字节码注入技术(BCI):
Heap Allocation Profiling(heap=sites)的例子:
Heap Dump(heap=dump)的例子, 它比上面的 Heap Allocation Profiling 能生成更详细的 Heap Dump 信息:
虽然在 JVM 启动参数中加入 - Xrunprof:heap=sites 参数可以生成 CPU/Heap Profile 文件, 但对 JVM 性能影响非常大, 不建议在线上服务器环境使用.
来源: http://www.jianshu.com/p/90a1e2593ec3