Java 作为最流行的编程语言之一,其应用性能诊断一直受到业界广泛关注。可能造成 Java 应用出现性能问题的因素非常多,例如线程控制、磁盘读写、数据库访问、网络I/O、垃圾收集等。想要定位这些问题,一款优秀的性能诊断工具必不可少。
使用数据说明问题,使用知识分析问题,使用工具处理问题。
无监控、不调优!
jps(Java Process Status):显示指定系统内所有的HotSpot虚拟机进程(查看虚拟机进程信息),可用于查询正在运行的虚拟机进程。
说明:对于本地虚拟机进程来说,进程的本地虚拟机ID与操作系统的进程ID是一致的,是唯一的。
基本使用语法为:jps [options] [hostid]
补充:如果某 Java 进程关闭了默认开启的UsePerfData参数(即使用参数-XX:-UsePerfData),那么jps命令(以及下面介绍的jstat)将无法探知该Java 进程。
hostid参数
RMI注册表中注册的主机名。如果想要远程监控主机上的 java 程序,需要安装 jstatd。
对于具有更严格的安全实践的网络场所而言,可能使用一个自定义的策略文件来显示对特定的可信主机或网络的访问,尽管这种技术容易受到IP地址欺诈攻击。
如果安全问题无法使用一个定制的策略文件来处理,那么最安全的操作是不运行jstatd服务器,而是在本地使用jstat和jps工具。
jstat(JVM Statistics Monitoring Tool):用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。常用于检测垃圾回收问题以及内存泄漏问题。
官方文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html
基本语法
基本使用语法为:jstat -
其中vmid是进程id号,也就是jps之后看到的前面的号码。
垃圾回收相关的:
JIT相关的:
jstat -class
Loaded:加载类的个数
Bytes:加载类的大小
Unloaded:卸载类的个数
Bytes:卸载类的大小
Time:装载和卸载类所花费的时间
interval参数: 用于指定输出统计数据的周期,单位为毫秒。即:查询间隔
如:每隔一秒打印一次
count参数: 用于指定查询的总次数
-t参数: 可以在输出信息前加上一个Timestamp列,显示程序运行了多长时间。单位:秒
-h参数: 可以在周期性数据输出时,输出多少行数据后输出一个表头信息
每三行输出一个表头
jstat -compiler
显示JIT编译器编译过的方法、耗时等信息
jstat -printcompilation
输出已经被JIT编译的方法
jstat还可以用来判断是否出现内存泄漏。
第1步:在长时间运行的 Java 程序中,我们可以运行jstat命令连续获取多行性能数据,并取这几行数据中 OU 列(即已占用的老年代内存)的最小值。
第2步:然后,我们每隔一段较长的时间重复一次上述操作,来获得多组 OU 最小值。如果这些值呈上涨趋势,则说明该 Java 程序的老年代内存已使用量在不断上涨,这意味着无法回收的对象在不断增加,因此很有可能存在内存泄漏。
jinfo(Configuration Info for Java):查看虚拟机配置参数信息,也可用于调整虚拟机的配置参数。
在很多情况卡,Java应用程序不会指定所有的Java虚拟机参数。而此时,开发人员可能不知道某一个具体的Java虚拟机参数的默认值。在这种情况下,可能需要通过查找文档获取某个参数的默认值。这个查找过程可能是非常艰难的。但有了jinfo工具,开发人员可以很方便地找到Java虚拟机参数的当前值。
基本使用语法为:jinfo [options] pid
jinfo -flag name
> jinfo -flag +PrintGCDetails 37514
> jinfo -flag PrintGCDetails 37514
-XX:+PrintGCDetails
> jinfo -flag -PrintGCDetails 37514
> jinfo -flag PrintGCDetails 37514
-XX:-PrintGCDetails
java -XX:+PrintFlagsInitial 查看所有JVM参数启动的初始值
java -XX:+PrintFlagsFinal 查看所有JVM参数的最终值
java -XX:+PrintCommandLineFlags 查看哪些已经被用户或者JVM设置过的详细的XX参数的名称和值
jmap(JVM Memory Map):作用一方面是获取dump文件(堆转储快照文件,二进制文件),它还可以获取目标Java进程的内存相关信息,包括Java堆各区域的使用情况、堆中对象的统计信息、类加载信息等。
官方帮助文档:https://docs.oracle.com/en/java/javase/11/tools/jmap.html
手动方式导出
jmap -dump:format=b,file=<filename.hprof> <pid>
jmap -dump:live,format=b,file=<filename.hprof> <pid>
自动方式
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=
由于jmap将访问堆中的所有对象,为了保证在此过程中不被应用线程干扰,jmap需要借助安全点机制,让所有线程停留在不改变堆中数据的状态。也就是说,由jmap导出的堆快照必定是安全点位置的。这可能导致基于该堆快照的分析结果存在偏差。
举个例子,假设在编译生成的机器码中,某些对象的生命周期在两个安全点之间,那么:live选项将无法探知到这些对象。
另外,如果某个线程长时间无法跑到安全点,jmap将一直等下去。与前面讲的jstat则不同,垃圾回收器会主动将jstat所需要的摘要数据保存至固定位置之中,而jstat只需直接读取即可。
jhat(JVM Heap Analysis Tool):
Sun JDK提供的jhat命令与jmap命令搭配使用,用于分析jmap生成的heap dump文件(堆转储快照)。jhat内置了一个微型的HTTP/HTML服务器,生成dump文件的分析结果后,用户可以在浏览器中查看分析结果(分析虚拟机转储快照信息)。
使用了jhat命令,就启动了一个http服务,端口是7000,即http://localhost:7000/,就可以在浏览器里分析。
说明:jhat命令在JDK9、JDK10中已经被删除,官方建议用VisualVM代替。
基本适用语法:jhat
jstack(JVM Stack Trace):用于生成虚拟机指定进程当前时刻的线程快照(虚拟机堆栈跟踪)。线程快照就是当前虚拟机内指定进程的每一条线程正在执行的方法堆栈的集合。
生成线程快照的作用:可用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。这些都是导致线程长时间停顿的常见原因。当线程出现停顿时,就可以用jstack显示各个线程调用的堆栈情况。
官方帮助文档:https://docs.oracle.com/en/java/javase/11/tools/jstack.html
在thread dump中,要留意下面几种状态
在JDK 1.7以后,新增了一个命令行工具jcmd。
它是一个多功能的工具,可以用来实现前面除了jstat之外所有命令的功能。比如:用它来导出堆、内存使用、查看Java进程、导出线程信息、执行GC、JVM运行时间等。
官方帮助文档:https://docs.oracle.com/en/java/javase/11/tools/jcmd.html
jcmd拥有jmap的大部分功能,并且在Oracle的官方网站上也推荐使用jcmd命令代jmap命令
之前的指令只涉及到监控本机的Java应用程序,而在这些工具中,一些监控工具也支持对远程计算机的监控(如jps、jstat)。为了启用远程监控,则需要配合使用jstatd 工具。命令jstatd是一个RMI服务端程序,它的作用相当于代理服务器,建立本地计算机与远程监控工具的通信。jstatd服务器将本机的Java应用程序信息传递到远程计算机。