【JVM进阶之路】九:性能监控工具-可视化工具篇
在前面已经学习了JVM性能监控的命令行工具,接下来学习JVM性能监控的命令行工具,通过可视化工具可以更直观地监控JVM性能、处理JVM相关问题。
1、JConsole
JConsole( Java Monitoring and Management Console),是一款基于 JMX( Java Manage-ment Extensions) 的可视化监视管理工具。
它的功能主要是对系统进行收集和参数调整,不仅可以用在虚拟机本身的管理上,还可以用于运行于虚拟机之上的软件中。
1.1、JConsole连接Java程序
JConsole程序位于%JAVA_HOME%bin目录下,直接通过命令启动。
在新建连接对话框中,罗列了所有的本地Java应用程序,选择需要连接的程序即可。
下面还有一个用于连接远程进程的文本框,输入正确的远程地址即可连接。
如果一个程序需要使用JConsole与那成连接,则需要在启动Java程序时,加上以下参数:
JAVA_OPTS="-Dfile.encoding=UTF-8"
JAVA_OPTS="$JAVA_OPTS -Dlog.dir=$LOG_PATH"
JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=xxx.xxx.xxx.xxx(本机IP) -Dcom.sun.management.jmxremote"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=xx"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.local.only=false"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.ssl=false"
1.2、Java程序概况
使用JConsole连接了一个本地程序,在概述
可以看到Java程序运行的概览信息,包括堆内存使用情况
、线程
、类
、CPU使用情况
四项信息的曲线图。
1.3、内存监控
内存
的作用相当于可视化的jstat命令,用于监视被收集器管理的虚拟机内存。
它不仅包含堆内存的整体信息,更细化到eden区、suvivior区、老年代的使用情况。
为了更加清晰地查看内存地变化,运行下面一段程序,然后连接:
/**
* VM参数: -Xms100m -Xmx100m -XX:+UseSerialGC
*/
public class JConcoleRAMMonitor {
/***
* 内存占位符对象,一个OOMObject大约占64KB
*/
static class OOMObject {
public byte[] placeholder = new byte[64 * 1024];
}
public static void fillHeap(int num) throws InterruptedException {
List<OOMObject> list = new ArrayList<OOMObject>();
for (int i = 0; i < num; i++) {
// 稍作延时,令监视曲线的变化更加明显
Thread.sleep(300);
list.add(new OOMObject());
}
System.gc();
}
public static void main(String[] args) throws Exception {
fillHeap(2000);
}
}
这段代码的作用是以64KB/50ms的速度向Java堆中填充数据,一共填充1000次。
观察Eden区的运行趋势,发现呈折线。观察堆内存使用,发现以稍有曲折的状态向上增长。
执行System.gc()之后,老年代的柱状图仍然显示峰值状态,最后程序会以堆内存溢出结束,这是因为空间未能回收——List<OOMObject>list对象一直存活, fillHeap()方法仍然没有退出,如果把 System.gc()移动到fillHeap()方法外调用就可以回收掉全部内存。
1.4、线程监控
JConcole还可以监控线程,相当于可视化的jstack命令。如图,JConcole显示了系统内的线程数量,并在屏幕下方显示了程序中所有的线程。单击线程名称,就可以查看线程的栈信息。
使用JConsole还可以快速定位死锁问题。
这是一段会产生死锁的代码:
public class ThreadLockDemo {
/**
* 线程死锁等待演示
*/
static class SynAddRunalbe implements Runnable {
int a, b;
public SynAddRunalbe(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public void run() {
synchronized (Integer.valueOf(a)) {
synchronized (Integer.valueOf(b)) {
System.out.println(a + b);
}
}
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new SynAddRunalbe(1, 2)).start();
new Thread(new SynAddRunalbe(2, 1)).start();
}
}
}
出现线程死锁以后,点击JConsole线程面板的检测到死锁
按钮,将会看到线程的死锁信息。
可以看到线程Thread-199等待线程Thread-21持有的资源。
1.5、类加载情况
如图,类页面显示了已经装载的类数量。在详细信息栏中,还显示了已经卸载的类的数量。
1.6、虚拟机信息
在VM摘要
,JConsole显示了当前应用程序的运行环境,包括虚拟机类型、版本、堆信息以及虚拟机参数等。
2、VisualVM
VisualVM(All-in-One Java Troubleshooting Tool)是功能最强大的运行监视和故障处理程序之一,曾经在很长一段时间内是Oracle官方主力发展的虚拟机故障处理工具。
相比一些第三方工具,VisualVM有一个很大的优点:不需要被监视的程序基于特殊Agent去运行,因此它的通用性很强,对应用程序实际性能的影响也较小,使得它可以直接应用在生产环境中。
2.1、VisualVM安装插件
在JDK6 Update7以后,VisualVM便作为JDK的一部分发布,它在%JAVA_HOME%bin 目录下,点击就可以启动。
VisualVM的精华之处在于它的插件。插件安装可以手动安装或者自动安装。
手动安装,从地址 https://visualvm.github.io/pluginscenters.html 下载载nbm包,点击“工具->插件->已下载”菜单,然后在弹出对话框中指定nbm包路径便可完成安装。
一般选择自动安装,点击工具
-> 插件菜单
,在可用插件
里可以看到可安装的插件,按需安装即可。
VisualVM中概述
,监视
、线程
,MBeans
的功能与前面介绍的JConsole差别不大,这里就不在赘言。
2.2、生成、浏览堆转储快照
在VisualVM中生成堆转储快照文件有两种方式,可以执行下列任一操作:
- 在
应用程序
窗口中右键单击应用程序节点,然后选择堆Dump
。 - 在
应用程序
窗口中双击应用程序节点以打开应用程序标签,然后在“监视”标签中单击堆Dump
。
生成堆转储快照文件之后,该堆的应用程序下增加了一个以[heap-dump]开头的子节点。如果需要把堆转储快照保存或发送出去,就需要heapdump节点上右键选择“另存为”菜单,否则当VisualVM关闭时,生成的堆转储快照文件会被当作临时文件自动清理掉。要打开一个由已经存在的堆转储快照文件,通过文件菜单中的“装入”功能,选择磁盘上的文件即可。
2.3、分析程序性能
要开始性能分析,先选择“CPU”和“内存”按钮中的一个,然后切换到应用程序中对程序进行操作,VisualVM会记录这段时间中应用程序执行过的所有方法。
如果是进行处理器执行时间分析,将会统计每个方法的执行次数、执行耗时;
如果是内存分析,则会统计每个方法关联的对象数以及这些对象所占的空间。
等要分析的操作执行结束后,点击“停止”按钮结束监控过程。
2.4、BTrace动态日志跟踪
BTrace是个很有意思的插件,它可以在不停机的情况下,通过字节码注入动态监控系统的运行情况。
Btrace自动安装如下,到github的网络可能存在不稳定的问题,可以重试,或者手动安装
在VisualVM中安装了BTrace插件后,在应用程序面板中右击要调试的程序,会出现“Trace Application…”菜单:
点击将进入BTrace面板。这个面板看起来就像一个简单的Java程序开发环境:
现在来尝试使用BTrace追踪正在运行的程序。
一段简单的Java代码:产生两个1000以内的随机整数,输出这两个数字相加的结果。
public class BTraceTest {
public int add(int a, int b) {
return a + b;
}
public static void main(String[] args) throws IOException {
BTraceTest test = new BTraceTest();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
for (int i = 0; i < 10; i++) {
reader.readLine();
int a = (int) Math.round(Math.random() * 1000);
int b = (int) Math.round(Math.random() * 1000);
System.out.println(test.add(a, b));
}
}
}
运行程序,现在需要在不停止程序的情况下,监控程序中生成的两个随机数。在VisualVM中打开该程序的监视,在BTrace页 签填充TracingScript的内容,输入调试代码:
/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class TracingScript {
@OnMethod(clazz = "cn.fighter3.test.BTraceTest",
method = "add",
location = @Location(Kind.RETURN)
)
public static void func(@Self cn.fighter3.test.BTraceTest instance,
int a, int b,
@Return int result) {
println("调用堆栈:");
jstack();
println(strcat("方法参数A:", str(a)));
println(strcat("方法参数B:", str(b)));
println(strcat("方法结果:", str(result)));
}
}
点击start按钮,当程序运行时将会在Output面板输出调试信息:
BTrace的用途很广泛,打印调用堆栈、参数、返回值只是它最基础的使用形式,更多应用可以查看官方仓库 https://github.com/btraceio/btrace/wiki 。
3、Java Mission Control
JMV最初是JRockit虚拟机提供的一款诊断工具。在Oracle JDK7 Update 40以后,它就绑定在Oracle JDK中发布。
JMC位置是%JAVA_HOME%/bin/jmc.exe
,打开软件界面:
在左侧的“JVM浏览器”面板中自动显示了通过JDP协议(Java Discovery Protocol)找到的本机正在运行的HotSpot虚拟机进程。
3.1、MBean服务器
点击本地进程的MBean服务器
:
可以看到,以飞行仪表的视图显示了Java堆使用率,CPU使用率和Live Set+Fragmentation。
3.2、飞行记录器(Flight Recorder)
飞行记录器是JMC提供的另一大功能,它通过记录程序在一段时间内的运行情况,将记录结果进行分析和展示,可以更进一步对系统的性能进行分析和诊断。
要使用JFR,程序启动需要带以下参数:
-XX:+UnlockCommercialFeatures -XX:+FlightRecorder
连接加了相关参数启动的程序,启动飞行记录,进行一分钟的性能记录:
记录结束后,JMC会自动打开刚才的记录:
JFR提供的数据质量通常也要比其他工具通过代理形式采样获得或者从MBean中取得的数据高得多。以垃圾搜集为例,HotSpot的MBean中一般有各个分代大小、收集次数、时间、占用率等数据(根据收集器不同有所差别),这些都属于“结果”类的信息,而JFR中还可以看到内存中这段时间分配了哪些对象、哪些在TLAB中(或外部)分配、分配速率 和压力大小如何、分配归属的线程、收集时对象分代晋升的情况等。
4、第三方工具
以上三个都是JDK自带的性能监控工具,除此之外还有一些第三方的性能监控工具。
- MAT
Java 堆内存分析工具。
- GChisto
GC 日志分析工具。
- GCViewer
GC
日志分析工具。
- JProfiler
商用的性能分析利器。
- arthas
阿里开源诊断工具。
- async-profiler
Java 应用性能分析工具,开源、火焰图、跨平台。
这里只是简单罗列,就不再展开详细介绍。
参考:
【1】:周志明编著《深入理解Java虚拟机:JVM高级特性与最佳实践》
【2】:《实战JAVA虚拟机 JVM故障诊断与性能优化》