JVM 第四篇:可视化 JVM 故障处理工具
本文内容过于硬核,建议有 Java 相关经验人士阅读。
1. 可视化工具
在 JDK 中为我们提供了大量的 JVM 故障处理工具,都在 JDK 的 bin 目录下:
这其中除了大量的命令行工具以外,还为我们提供了更加方便快捷的可视化工具,主要是以下这 4 个:
- JConsole: 最古老的工具,早在 JDK 5 时期就已经存在的虚拟机监控工具。
- JHSDB: 名义上在 JDK 9 中才正式提供,但之前已经以 sa-jdi.jar 包里面的 HSDB(可视化工具) 和 CLHSDB(命令行工具) 的形式存在了很长一段时间。
- VisualVM: 在 JDK 6 Update 7 中首次发布,直到 JRockit Mission Control 与 OracleJDK 的融合工作完成之前,它都曾是 Oracle 主力推动的多合一故障处理工具,现在它已经从 OracleJDK 中分离出来,成为一个独立发展的开源项目。
- JMC: Java Mission Control ,曾经是大名鼎鼎的来自 BEA 公司的图形化诊断工具,随着 BEA 公司被 Oracle 收购,它便被融合进 OracleJDK 之中。在 JDK 7 Update 40 时开始随 JDK 一起发布,后来 Java SE Advanced 产品线建立, Oracle 明确区分了 Oracle OpenJDK 和 OracleJDK 的差别, JMC 从 JDK 11 开始又被移除出 JDK 。
2. HSDB
HSDB(Hotspot Debugger) 是 JDK 自带的工具,用于查看 JVM 运行时的状态。
使用方式由于在 JDK 9 之前没有正式提供,所以也未在 JDK 的 bin 目录下提供直接可执行文件,需要在命令行执行命令才能启动。
首先在命令行中先 cd 至 C:\Program Files\Java\jdk1.8.0_221\lib
目录,然后执行:
java -cp .\sa-jdi.jar sun.jvm.hotspot.HSDB
我在执行这句命令的时候报了个错:
Exception in thread "Thread-1" java.lang.UnsatisfiedLinkError: Can't load library: C:\Program Files\Java\jdk-11.0.4\bin\sawindbg.dll
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2620)
at java.base/java.lang.Runtime.load0(Runtime.java:767)
at java.base/java.lang.System.load(System.java:1831)
at sun.jvm.hotspot.debugger.windbg.WindbgDebuggerLocal.<clinit>(WindbgDebuggerLocal.java:661)
at sun.jvm.hotspot.HotSpotAgent.setupDebuggerWin32(HotSpotAgent.java:567)
at sun.jvm.hotspot.HotSpotAgent.setupDebugger(HotSpotAgent.java:335)
at sun.jvm.hotspot.HotSpotAgent.go(HotSpotAgent.java:304)
at sun.jvm.hotspot.HotSpotAgent.attach(HotSpotAgent.java:140)
at sun.jvm.hotspot.HSDB.attach(HSDB.java:1184)
at sun.jvm.hotspot.HSDB.access$1700(HSDB.java:53)
at sun.jvm.hotspot.HSDB$25$1.run(HSDB.java:456)
at sun.jvm.hotspot.utilities.WorkerThread$MainLoop.run(WorkerThread.java:66)
at java.base/java.lang.Thread.run(Thread.java:834)
含义是有一个 sawindbg.dll
在 jdk 的目录下找不到,因为我本地有多个 jdk ,配置环境变量的是 jdk 11 ,我在 jdk 8 的 jre 的 bin 目录下找到了这个文件,直接 copy 到 jdk 11 的bin 目录下解决此问题。
执行完成后会打开这么个界面:
接下来,我们写一小段测试代码:
public abstract class A {
public void printMe() {
System.out.println("I am A class");
}
public abstract void sayHello();
}
public class B extends A {
@Override
public void sayHello() {
System.out.println("I am B class");
}
}
public class HSDB_Test {
public static void main(String[] args) throws IOException {
A obj = new B();
// 无意义,单纯用作卡住主线程,防止线程结束
System.in.read();
System.out.println(obj);
}
}
首先在命令行中使用命令 jps -l
查看 Java 进程的 pid :
PS C:\Users\inwsy> jps -l
1648 com.geekdigging.lesson04.jvmtools.HSDB_Test
8704
9280 org.jetbrains.jps.cmdline.Launcher
14724 jdk.jcmd/sun.tools.jps.Jps
3220 org/netbeans/Main
15144
20572 org.jetbrains.jps.cmdline.Launcher
7548 sun.jvm.hotspot.HSDB
可以看到,我们刚写的测试方法的 pid 是 1648 ,在 HSDB 中点击 File > Attach to Hotspot process :
第一个看到的就是当前进程中的线程:
到这里,我们的准备工作就已经结束,接着我们使用 Tools > Class Browser 找到对象 B 的内存地址:
图中红框框起来的是我自己写的三个类,可以看到我这里 B 的内存地址是 0x00000007c0060c18
。
接下来,使用 Tools > Inspector 查看这个对象的详细信息:
vtable 是虚表方法,这里我们看到 class B 有 7 个虚表方法,因为所有的对象都继承自 Object ,所以 B 继承了 Object 的 5 个方法,然后还继承了 A 的一个方法,自己重写了一个方法,总共是 7 个方法。
这个我们可以进行一下验证,可以在 Windows > Console 中使用 mem 命令进行查看。
那么我们可以开始计算, vtable 是在 instanceKlass 对象实例的尾部,而 instanceKlass 大小在 64 位系统的大小为 0x1B8 ,因此 vtable 的起始地址等于 instanceKlass 的内存首地址加上 0x1B8 等于 7C0060DD0 。
接下来,我们在 Windows > Console 中使用 mem 命令进行验证:
第一列是方法实际在堆中的内存地址,第二列则是内存指针地址,我们可以将拿到的内存指针地址去 A , B 和 Object 中分别查看,可以看到前 5 行对应的是 Object 的方法,第 6 行对应的是 A 对象中的方法,第 7 行则对应 B 对象中的方法。
3. JConsole
JConsole(Java Monitoring and Management Console) 是一款基于 JMX(Java Manage-ment Extensions) 的可视化监视、管理工具。它的主要功能是通过 JMX 的 MBean(Managed Bean) 对系统进行信息收集和参数动态调整。
JConsole 位于 JDK/bin 这个目录下,直接双击 jconsole.exe 就可以直接启动,在启动之后,会自动搜索出当前在本机运行的所有虚拟机进程。
这里可以看到我本机目前运行了一个 JConsole ,一个 idea ,还有一个启动的 tomcat 的源码。
随便双击一个服务,进入主页面:
可以看到主界面里共包括概述、内存、线程、类、 VM 摘要、 MBean 六个页签。
还是来个小示例,我们来了解下它的监控功能。
public class MonitoringTest {
// 内存占位对象,一个对象大约 64KB
static class OOMObject {
public byte[] placeholder = new byte[64 * 1024];
}
public static void fillHeap(int nums) throws InterruptedException {
List<OOMObject> list = new ArrayList<>();
for (int i = 0; i < nums; i++) {
Thread.sleep(50);
list.add(new OOMObject());
}
System.gc();
}
public static void main(String[] args) throws InterruptedException {
fillHeap(1000);
}
}
这个案例是使用大约 64KB/50ms 的速度向 Java 堆中填充数据,一共填充 1000 次。
程序执行后可以看到,在整个 Java 堆中,曲线一直是平滑向上的。
切换到内存标签页,查看 Eden 后可以发现,整个 Eden 的图形是一个折线:
再切换到 Gen ,可以看到整个老年代也是折叠向上的:
我们已经在代码里加了 System.gc()
,为什么看起来没生效呢?
因为 System.gc()
是在 fillHeap()
方法中的,在 GC 的时候,还在作用域中,想要正常回收老年代,需要将 System.gc()
这段代码转移到 fillHeap()
外面。
先修改下代码:
public static void main(String[] args) throws InterruptedException {
fillHeap(1000);
System.gc();
// GC 后停顿 3s ,方便观察图像
Thread.sleep(3000);
}
可以看到在最后进程结束的时候, Gen 的柱状图已经没有内存占用了,内存回收成功。
3. VisualVM
VisualVM(All-in-One Java Troubleshooting Tool)是功能最强大的运行监视和故障处理程序之一,曾经在很长一段时间内是 Oracle 官方主力发展的虚拟机故障处理工具。
VisualVM 同样在 JDK/bin 这个目录下,双击 jvisualvm.exe 即可运行。在启动之后,直接在左侧会显示当前在本机运行的所有虚拟机进程。
VisualVM 基于 NetBeans 平台开发工具,所以一开始它就具备了通过插件扩展功能的能力,有了插件扩展支持, VisualVM 可以做到:
- 显示虚拟机进程以及进程的配置、环境信息(jps、jinfo)。
- 监视应用程序的处理器、垃圾收集、堆、方法区以及线程的信息(jstat、jstack)。
- dump 以及分析堆转储快照(jmap、jhat)。
- 方法级的程序运行性能分析,找出被调用最多、运行时间最长的方法。
- 离线程序快照:收集程序的运行时配置、线程 dump 、内存 dump 等信息建立一个快照,可以将快照发送开发者处进行 Bug 反馈。
- 其他插件带来的无限可能性。
VisualVM 的插件可以在 工具->插件 中联网后直接安装。
我这里只安装了两个最常用的,一个是 GC 监控的插件,还有一个可以动态插入调试程序的插件。
我这里使用最常用的开发工具 IDEA 启动过程演示一下通过 VisualVM 监控程序 GC 。
首先我们启动 IDEA ,直到 IDEA 可以正常操作,看下 VisualVM 的 GC 监控。
在主信息面板,可以看到 IDEA 所使用 JVM 的版本信息,可以看到具体的 JAVA_HOME 路径,还可以看到具体的 JVM 参数,这里可以看到 IDEA 启动时设置的默认最小堆和最大堆内存的设置分别是 128MB 和 750 MB ,所使用的垃圾回收器则是 CMS 收集器。
然后点击 Visual GC,可以看到:
在启动过程中, Class 加载消耗了 28s 左右,而 Class 编译则消耗了 35s 。并且在这个过程中, Minor GC 被触发了 149 次,消耗只有 713ms ,我们更加关注的 Full GC 更是一次都没有触发,消耗为 0 。
因为 IDEA 默认使用的是 CMS 收集器,如果我们换成 G1 收集器会不会更快一些呢?
首先,找到 IDEA 的配置文件,我的 IDEA 是通过 Toolbox 进行安装的,所以我的 IDEA 的配置文件的路径有点奇怪 D:\Program Files\JetBrains\apps\IDEA-U\ch-0\202.7660.26.vmoptions
。
先把这个文件备份到桌面一个,防止改坏了导致 IDEA 不能使用。
删掉现有的垃圾回收器配置 -XX:+UseConcMarkSweepGC
,增加 G1 收集器的配置:
-XX:+UseG1GC
其余的配置不做修改,直接关闭 IDEA 重启,再看下 GC 情况。
首先先看下主面板,看下我们的 GC 收集器是否已经切换成功:
然后再看下 GC 面板:
Minor GC 竟然被触发了 271 次,而且消耗达到了 853ms ,好吧,看来在客户端还是更适合使用 CMS 做为垃圾回收器。
我们再修改下 -Xmx 这个配置,将配置的大小缩减为现在的一半,再把 GC 换回原有的 CMS ,看下 Full GC 的情况:
可以看到, Full GC 整整发生了 46 次,并且耗时超过了 21s ,而且这是 IDEA 的界面上也开始弹出警告,警告我们内存不足了,需要调整。
吓得我赶紧改回了原有配置,顺便把 -Xmx 的大小加到了 1024 ,尽量减少 Full GC 的情况。
4. Java Mission Control
JMC 同样在 JDK/bin 这个目录下,双击 jmc.exe 即可运行。
打开后在 JVM 浏览器面板中有两个选项,一个是 MBean ,一个是 JFR 飞行记录器。
关于 MBean 这部分数据,与 JConsole 和 VisualVM 上取到的内容是一样的,只是展示形式上有些差别,就不多说了。
双击「飞行记录器」,将会出现「飞行记录器」窗口(如果第一次使用,还会收到解锁商业功能的警告窗)。
注意:在使用前需要在 JVM 中增加如下两个参数,含义是解锁 JFR 功能的锁定。
-XX:+UnlockCommercialFeatures
-XX:+FlightRecorder
飞行记录报告里包含以下几类信息:
- 一般信息:关于虚拟机、操作系统和记录的一般信息。
- 内存:关于内存管理和垃圾收集的信息。
- 代码:关于方法、异常错误、编译和类加载的信息。
- 线程:关于应用程序中线程和锁的信息。
- I/O:关于文件和套接字输入、输出的信息。
- 系统:关于正在运行Java虚拟机的系统、进程和环境变量的信息。
- 事件:关于记录中的事件类型的信息,可以根据线程或堆栈跟踪,按照日志或图形的格式查看。
5. 小结
这 4 款可视化工具看下来,个人感觉还是最后一个 JMC 对使用者来讲最友好, MBean 数据源展示了大量的当前 JVM 的信息,而且全都以图表的形式进行了展现,更加给力还是它的 JFR 功能,可以记录一段时间内所有的操作,并且以图表的形式进行展现,对我们分析问题时候的帮助无疑是巨大的。
当然,喜欢用哪款工具完全是个人喜好,比如 VisualVM 也很强大,可能它本身的功能没那么强,但是它可以安装插件,完全根据需要进行插件的安装,这个玩法非常 DIY ,总的算下来,我还是喜欢使用 VisualVM 更多一些。
参考
《深入理解Java虚拟机:JVM高级特性与最佳实践_周志明》
https://www.cnblogs.com/alinainai/p/11070923.html
JVM 第四篇:可视化 JVM 故障处理工具的更多相关文章
- JVM(4) 虚拟机性能监控与故障处理工具
1. Sun JDK 监控和故障处理工具 1)jps:JVM process Status Tool,显示指定系统内所有的HotSpot虚拟机进程.可以列出正在运行的虚拟机进程,并显示虚拟机执行主类( ...
- 【搞定Jvm面试】 JDK监控和故障处理工具揭秘
本文已经收录自笔者开源的 JavaGuide: https://github.com/Snailclimb ([Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识)如果觉得不错 ...
- 【JVM第四篇--运行时数据区】堆
写在前面的话:本文是在观看尚硅谷JVM教程后,整理的学习笔记.其观看地址如下:尚硅谷2020最新版宋红康JVM教程 一.堆的概述 JVM的运行时数据区如下: 一个Java程序运行起来对应着一个进程(操 ...
- 深入理解Java虚拟机之读书笔记四 性能监控与故障处理工具
JDK的bin目录下存在很多有效的命令行工具,它们就是jdk\lib\toos.jar类库的封装. 一.jps:虚拟机进程状况工具,查询出LVMID. 二.jstat:虚拟机统计信息监视工具, 三.j ...
- 深入理解JVM(四)JVM性能监控与故障处理工具
4.2 JVM命令行工具 1.jps(JVM Process Status Tool):JVM进程查看工具,命令行进入到jdk的bin目录下,使用命令:jps -l/-q/-v/-m 2.jstat( ...
- JVM 第五篇:命令行 JVM 故障处理工具
本文内容过于硬核,建议有 Java 相关经验人士阅读. 1. 引言 虽然我们前面介绍了各种图形化 JVM 故障处理工具,但是很多情况下,我们在处理问题的时候并没有图形化的操作环境可以使用,这时候,就需 ...
- JVM学习总结五——性能监控及故障处理工具
之前扯了四篇理论,这一篇终于可以动动手了.本篇我们将介绍JVM常用的一些工具,这些工具将是我们监控JVM状态.处理故障和调优分析的利器. 不过在开始之前,我还是要先车扯两句:工具终归只是帮助我们我们处 ...
- JVM故障处理工具,使用总结
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 用都用不到怎么学? 没有场景.没有诉求,怎么学习这些似乎用不上知识点. 其实最好的方 ...
- jvm性能监控与故障处理工具
jdk为我们提供了一系列的jvm性能监控和故障处理工具,在这里根据学习进度进行整理记录.便于之后查阅 1.jps 虚拟机进程工具 类似于Linux系统中的ps命令,用于查看虚拟机进程,常用的有以下功 ...
随机推荐
- 如何自制WC3地形纹理贴图
http://world-editor-tutorials.thehelper.net/tilesets.php https://wenku.baidu.com/view/e761c953cc1755 ...
- 【jmespath】—2. 进阶 List and Slice Projections
Projections翻译的话说实话我也不知道翻成什么比较准确,姑且就叫"投影"吧,不过带来的功能确实非常的强大.好用. 首先,我先说下我的理解: 通配符 * 通配符生成的是一个l ...
- Docker 搭建 Redis Cluster 集群环境
使用 Docker 搭建 Redis Cluster,最重要的环节就是容器通信的问题,这一块我们在之前的文章中已经给大家解决了<Docker 网络模式详解及容器间网络通信>,本篇文章主要练 ...
- 10_Python的函数function
1.函数的概述 1.函数是可以重复执行的语句块且可以重复调用,函数封装了可重复执行的语句提高了语句的可重复性 2.函数的参数和返回值的作用流程图: https://www.processon. ...
- vue相关知识点及面试
### vue #### vue生命周期 beforeCreated `实例初始化,数据观察和event/watch事件配置之前被调用` created `实例创建后立即调用,数据观测,数据和方法运算 ...
- 给MySQL中数据表添加字段
添加一个char字段: mysql> alter table stock add src char(20); Query OK, 3766 rows affected (0.65 sec) Re ...
- 索引对单列极值查询的显著性影响(百万级别表单列最值查询 Cost由1405变成3)
表结构: create table hy_emp( id number(7,0) primary key, name nvarchar2(20) not null, salary number(5,0 ...
- java中整型、浮点型、char型扩展
怎么区分是什么进制? 二进制:0b开头,eg: int i = 0b10; 八进制:0开头,eg: int k = 010; 十进制: 十六进制:0x开头,eg: int j = 0x10; 浮点数类 ...
- linux基础命令一、
命令格式: 命令 -选项 参数 uname -r 查看内核版本 uname -m 查看系统版本 alias 别名. 举例: alias grep ='grep --color=auto' ...
- LVM最佳实践
LVM逻辑卷管理器 LVM概念 在Linux中,逻辑卷管理器(Logical Volume Manager, LVM)是为Linux内核提供逻辑卷管理的设备映射器目标.大多数现代Linux发行版都能够 ...