深入理解可视化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高级特性与最佳实践_周志明

到此这篇关于深入理解可视化JVM 故障处理工具的文章就介绍到这了,更多相关JVM 故障处理工具内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JVM如何处理异常深入详解

    前言 无论你是使用何种编程语言,在日常的开发过程中,都会不可避免的要处理异常.今天本文将尝试讲解一些JVM如何处理异常问题,希望能够讲清楚这个内部的机制,如果对大家有所启发和帮助,则甚好. 当异常不仅仅是异常 我们在标题中提到了异常,然而这里指的异常并不是单纯的Exception,而是更为宽泛的Throwable.只是我们工作中习以为常的将它们(错误地)这样称谓. 关于Exception和Throwable的关系简单描述一下 Exception属于Throwable的子类,Throwable的另

  • JVM处理未捕获异常的方法详解

    前言 继之前的文章详解JVM如何处理异常,今天再次发布一篇比较关联的文章,如题目可知,今天聊一聊在JVM中线程遇到未捕获异常的问题,其中涉及到线程如何处理未捕获异常和一些内容介绍. 什么是未捕获异常 未捕获异常指的是我们在方法体中没有使用try-catch捕获的异常,比如下面的例子 private static void testUncaughtException(String arg) { try { System.out.println(1 / arg.length()); } catch

  • 详解JVM类加载机制及类缓存问题的处理方法

    前言 大家应该都知道,当一个Java项目启动的时候,JVM会找到main方法,根据对象之间的调用来对class文件和所引用的jar包中的class文件进行加载(其步骤分为加载.验证.准备.解析.初始化.使用和卸载),方法区中开辟内存来存储类的运行时数据结构(包括静态变量.静态方法.常量池.类结构等),同时在堆中生成相应的Class对象指向方法区中对应的类运行时数据结构. 用最简单的一句话来概括,类加载的过程就是JVM根据所需的class文件的路径,通过IO流的方式来读取class字节码文件,并通

  • 深入理解可视化JVM 故障处理工具

    本文内容过于硬核,建议有 Java 相关经验人士阅读. 1. 可视化工具 在 JDK 中为我们提供了大量的 JVM 故障处理工具,都在 JDK 的 bin 目录下: 这其中除了大量的命令行工具以外,还为我们提供了更加方便快捷的可视化工具,主要是以下这 4 个: JConsole: 最古老的工具,早在 JDK 5 时期就已经存在的虚拟机监控工具. JHSDB: 名义上在 JDK 9 中才正式提供,但之前已经以 sa-jdi.jar 包里面的 HSDB(可视化工具) 和 CLHSDB(命令行工具)

  • 深入理解java虚拟机的故障处理工具

    前言 本文主要给大家介绍的是java虚拟机的故障处理工具,文中提到这些工具包括: 名称 主要作用 jps JVM process Status Tool, 显示指定系统内所有的HotSpot虚拟机进程.通常是本地主机 jstat JVM Statistics Monitoring Tool,用于收集HotSpot虚拟机各方面的运行数据 jinfo Configuration Info for java, 显示虚拟机配置信息 jmap Memory Map for Java, 生成虚拟机的内存存储

  • SpringBoot可视化接口开发工具magic-api的简单使用教程

    目录 magic-api简介 使用 在SpringBoot中使用 增删改查 参数验证 结果转换 使用事务 集成Swagger 总结 参考资料 magic-api简介 magic-api是一个基于Java的接口快速开发框架,编写接口将通过magic-api提供的UI界面完成,自动映射为HTTP接口,无需定义Controller.Service.Dao.Mapper.XML.VO等Java对象. 使用 下面我们来波实战,熟悉下使用magic-api来开发API接口. 在SpringBoot中使用 m

  • Docker可视化ui管理工具Portainer安装及使用解析

    Portainer是一款优秀的Docker图形化管理工具,提供状态显示面板.应用模板快速部署.容器镜像网络数据卷的基本操作(包括上传下载镜像,创建容器等操作).事件日志显示.容器控制台操作.Swarm集群和服务等集中管理和操作.登录用户管理和控制等功能.功能十分全面,安装起来也非常的简单,推荐给大家. 1.下载Portainer镜像 搜索portainer镜像: [root@iZbp13sno1lc2yxlhjc4b3Z /]# docker search portainer NAME DESC

  • 分享4个最受欢迎的大数据可视化工具

    想像阅读书本一样阅读数据流?这只有在电影中才有可能发生. 在现实世界中,企业必须使用数据可视化工具来读取原始数据的趋势和模式. 大数据可视化是进行各种大数据分析解决的最重要组成部分之一. 一旦原始数据流被以图像形式表示时,以此做决策就变得容易多了. 为了满足并超越客户的期望,大数据可视化工具应该具备这些特征: ·      能够处理不同种类型的传入数据 ·      能够应用不同种类的过滤器来调整结果 ·      能够在分析过程中与数据集进行交互 ·      能够连接到其他软件来接收输入数据

  • Python可视化工具如何实现动态图表

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理 以下文章来源于菜J学Python ,作者J哥 前言 这次呢,我想讲讲地图可视化的内容,以前我也写过用Python的内置库绘制地图,但总感觉不够美观.如何才能在短时间内制作漂亮的可视化地图呢,我觉得Python+可视化工具是不错的选择. 以下动态可视化地图就是J哥亲手绘制,展现了一段时间内广州市企事业单位在网上商城采购商品的分布及随时间的变化. 接下来,将手把手教你如何绘制这个动态

  • 深入理解JVM自动内存管理

    目录 一.前言 1.1 计算机==>操作系统==>JVM 1.1.1 虚拟与实体(对上图的结构层次分析) 1.1.2 Java程序执行(对上图的箭头流程分析) 二.JVM内存空间与参数设置 2.1 运行时数据区 2.2 关于StackOverflowError和OutOfMemoryError 2.2.1 StackOverflowError 2.2.2 OutOfMemoryError 2.3 JVM堆内存和非堆内存 2.3.1 堆内存和非堆内存 2.3.2 JVM堆内部构型(新生代和老年代

  • [JAVA]十四种Java开发工具点评

    在计算机开发语言的历史中,从来没有哪种语言象Java那样受到如此众多厂商的支持,有如此多的开发工具,Java菜鸟们如初入大观园的刘姥姥,看花了眼,不知该何种选择.的确,这些工具各有所长,都没有绝对完美的,就算是老鸟也很难做出选择.在本文中我简要介绍了常见的十四种Java开发工具的特点,管中窥"器",希望能对大家有所帮助. 1.JDK (Java Development Kit) 2.Java Workshop 3.NetBeans 与Sun Java Studio 5 4.Borlan

  • 老生常谈JVM的内存溢出说明及参数调整

    对于JVM的内存写过的文章已经有点多了,而且有点烂了,不过说那么多大多数在解决OOM的情况,于此,本文就只阐述这个内容,携带一些分析和理解和部分扩展内容,也就是JVM宕机中的一些问题,OK,下面说下OOM的常见情况: 第一类内存溢出,也是大家认为最多,第一反应认为是的内存溢出,就是堆栈溢出: 那什么样的情况就是堆栈溢出呢?当你看到下面的关键字的时候它就是堆栈溢出了: java.lang.OutOfMemoryError: ......java heap space..... 也就是当你看到hea

  • Win XP操作系统支持工具全接触

    无论是监视系统的性能,还是探究故障的原因,真正的关键是找到对症的工具.打开XP的"所有程序"菜单,"系统工具"下面就有一大堆工具让你选择.但是,如果你是一个富有经验的用户,Windows XP还为你准备了另一份大礼包:安装CD上隐藏着100多个五花八门的工具.它们涵盖了操作系统的方方面面,从网络和Internet连接到文件夹和磁盘管理,无所不备.这是一个称为Windows支持工具(Support Tools)的工具集,专门为那些不甘停留于Windows皮毛的用户而准

随机推荐