全功能内置分析器,如 JConsole 和 VisualVM 的成本有时比它们的性能费用还要高 — 尤为是在生产软件上运行的系统中。所以,在聚焦 Java 性能监控的第 2 篇文章中,我将介绍 5 个命令行分析工具,使开发人员仅关注运行的 Java 进程的一个方面。编程
JDK 包括不少命令行实用程序,能够用于监控和管理 Java 应用程序性能。虽然大多数这类应用程序都被标注为 “实验型”,在技术上不受支持,可是它们颇有用。有些甚至是特定用途工具的种子材料,可使用 JVMTI 或 JDI(参见 参考资料)创建。浏览器
不少命令行工具都要求您识别您但愿监控的 Java 进程。这与监控本地操做系统进程、一样须要一个程序识别器的同类工具没有太大区别。jsp
“VMID” 识别器与本地操做系统进程识别器(“pid”)并不老是相同的,这就是咱们须要 JDK jps
实用程序的缘由。ide
jps
— 名称反映了在大多数 UNIX 系统上发现的 ps
实用程序 — 告诉咱们运行 Java 应用程序的 JVMID。顾名思义,jps
返回指定机器上运行的全部已发现的 Java 进程的 VMID。若是 jps
没有发现进程,并不意味着没法附加或研究 Java 进程,而只是意味着它并未宣传本身的可用性。
若是发现 Java 进程,jps
将列出启用它的命令行。这种区分 Java 进程的方法很是重要,由于只要涉及操做系统,全部的 Java 进程都被统称为 “java
”。在大多数状况下,VMID 是值得注意的重要数字。
使用分析实用程序开始的最简单方法是使用一个如在demo/jfc/SwingSet2
中发现的 SwingSet2 演示同样的演示程序。这样就能够避免程序做为背景/监控程序运行时出现挂起的可能性。当您了解工具及其费用后,就能够在实际程序中进行试用。
加载演示应用程序后,运行 jps
并注意返回的 vmid
。为了得到更好的效果,采用 -Dcom.sun.management.jmxremote
属性集启动 Java 进程。若是没有使用该设置,部分下列工具收集的部分数据可能不可用。
jstat
实用程序能够用于收集各类各样不一样的统计数据。jstat
统计数据被分类到 “选项” 中,这些选项在命令行中被指定做为第一参数。对于 JDK 1.6 来讲,您能够经过采用命令 -options
运行 jstat
查看可用的选项清单。清单 1 中显示了部分选项:
清单 1. jstat 选项
-class -compiler -gc -gccapacity -gccause -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcpermcapacity -gcutil -printcompilation |
实用程序的 JDK 记录(参见 参考资料)将告诉您清单 1 中每一个选项返回的内容,可是其中大多数用于收集垃圾的收集器或者其部件的性能信息。-class
选项显示了加载及未加载的类(使其成为检测应用程序服务器或代码中 ClassLoader
泄露的重要实用程序,且 -compiler
和 -printcompilation
都显示了有关 Hotspot JIT 编译程序的信息。
默认状况下,jstat
在您核对信息时显示信息。若是您但愿每隔必定时间拍摄快照,请在 -options
指令后以毫秒为单位指定间隔时间。jstat
将持续显示监控进程信息的快照。若是您但愿 jstat
在终止前进行特定数量的快照,在间隔时间/时间值后指定该数字。
若是 5756 是几分钟前开始的运行 SwingSet2 程序的 VMID,那么下列命令将告诉 jstat
每 250 毫秒为 10 个佚代执行一次 gc 快照转储,而后中止:
jstat -gc 5756 250 10 |
请注意 Sun(如今的 Oracle)保留了在不进行任何预先通知的状况下更改各类选项的输出甚至是选项自己的权利。这是使用不受支持实用程序的缺点。请参看 Javadocs 了解 jstat
输出中每一列的所有细节。
了解 Java 进程及其对应的执行线程内部发生的状况是一种常见的诊断挑战。例如,当一个应用程序忽然中止进程时,很明显出现了资源耗尽,可是仅经过查看代码没法明确知道何处出现资源耗尽,且为何会发生。
jstack
是一个能够返回在应用程序上运行的各类各样线程的一个完整转储的实用程序,您可使用它查明问题。
采用指望进程的 VMID 运行 jstack
会产生一个堆转储。就这一点而言,jstack
与在控制台窗口内按 Ctrl-Break 键起一样的做用,在控制台窗口中,Java 进程正在运行或调用 VM 内每一个 Thread
对象上的 Thread.getAllStackTraces()
或Thread.dumpStack()
。jstack
调用也转储关于在 VM 内运行的非 Java 线程的信息,这些线程做为 Thread
对象并不老是可用的。
jstack
的 -l
参数提供了一个较长的转储,包括关于每一个 Java 线程持有锁的更多详细信息,所以发现(和 squash)死锁或可伸缩性 bug 是极其重要的。
有时,您正在处理的问题是一个对象泄露,如一个 ArrayList
(可能持有成千上万个对象)该释放时没有释放。另外一个更广泛的问题是,看似从不会压缩的扩展堆,却有活跃的垃圾收集。
当您努力寻找一个对象泄露时,在指定时刻对堆及时进行拍照,而后审查其中内容很是有用。jmap
经过对堆拍摄快照来提供该功能的第一部分。而后您能够采用下一部分中描述的 jhat
实用程序分析堆数据。
与这里描述的其余全部实用程序同样,使用 jmap
很是简单。将 jmap
指向您但愿拍快照的 Java 进程的 VMID,而后给予它部分参数,用来描述产生的结果文件。您要传递给 jmap
的选项包括转储文件的名称以及是否使用一个文本文件或二进制文件。二进制文件是最有用的选项,可是只有当与某一种索引工具 结合使用时 — 经过十六进制值的文本手动操做数百兆字节不是最好的方法。
随意看一下 Java 堆的更多信息,jmap
一样支持 -histo
选项。-histo
产生一个对象文本柱状图,如今在堆中大量引用,由特定类型消耗的字节总数分类。它一样给出了特定类型的总示例数量,支持部分原始计算,并猜想每一个实例的相对成本。
不幸的是,jmap
没有像 jstat
同样的 period-and-max-count 选项,可是将 jmap
(或 jmap.main()
)调用放入 shell 脚本或其余类的循环,周期性地拍摄快照相对简单。(事实上,这是加入 jmap
的一个好的扩展,不论是做为 OpenJDK 自己的源补丁,仍是做为其余实用程序的扩展。)
5. jhat (com.sun.tools.hat.Main)
将堆转储至一个二进制文件后,您就可使用 jhat
分析二进制堆转储文件。jhat
建立一个 HTTP/HTML 服务器,该服务器能够在浏览器中被浏览,提供一个关于堆的 object-by-object 视图,及时冻结。根据对象引用草率处理堆可能会很是好笑,您能够经过对整体混乱进行某种自动分析而得到更好的服务。幸运的是,jhat
支持 OQL 语法进行这样的分析。
例如,对全部含有超过 100 个字符的 String
运行 OQL 查询看起来以下:
select s from java.lang.String s where s.count >= 100 |
结果做为对象连接显示,而后展现该对象的完整内容,字段引用做为能够解除引用的其余连接的其余对象。OQL 查询一样能够调用对象的方法,将正则表达式做为查询的一部分,并使用内置查询工具。一种查询工具,referrers()
函数,显示了引用指定类型对象的全部引用。下面是寻找全部参考 File
对象的查询:
select referrers(f) from java.io.File f |
您能够查找 OQL 的完整语法及其在 jhat
浏览器环境内 “OQL Help” 页面上的特性。将 jhat
与 OQL 相结合是对行为不当的堆进行对象调查的有效方法。
当您须要近距离观察 Java 进程内发生的事情时,JDK 的分析扩展会很是有用。本文中介绍的全部工具均可以从命令行中由其本身使用。它们还能够与 JConsole 或 VisualVM 有力地结合使用。JConsole 和 VisualVM 提供 Java 虚拟机的整体视图,jstat
和 jmap
等有针对性的工具支持您对研究进行微调。
走进 5 件事 系列: 编写脚本。