欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

JVM系列之:你知道Jhsdb整合的故障处理工具

最编程 2024-02-10 14:05:13
...

网络异常,图片无法展示
|

本文为《深入学习 JVM 系列》第二十一篇文章


Jhsdb 是 JDK9 引入的新的命令行工具,它有 clhsdb、debugd、hsdb、jstack、jmap、jinfo、jsnap 这些 mode 可以使用,其中有几个在名称和功能上与以前的 JDK 发行版中可用的各个命令行工具相对应。看得出来,官方想要 jhsdb 工具整合多个其他工具的功能,甚至还做了一些功能拓展。所以本文将带大家认识一下 jhsdb 下的这些 mode。


在使用 jhsdb 工具之前,必须先获取 PID,所以我们先来认识一下 jps 命令。


jps


jps(JVM Process Status Tool)可以列出正在运行的虚拟机进程,需要注意的是,jps 仅查找当前用户的 Java 进程,而不是当前系统中的所有进程。


jps [options] [hostid]
复制代码

在默认情况下,jps 的输出信息包括 Java 进程的进程 ID 以及主类名。我们还可以通过追加参数,来打印额外的信息。


  • -v:输出传递给 JVM 的参数(如-XX:+UnlockExperimentalVMOptions -XX:+UseZGC)
  • -l: 打印模块名以及包名
  • -m:将打印传递给主类的参数


或者根据进程名称查找准确的 id。


ps aux|grep xxxx
复制代码


需要注意的是,如果某 Java 进程关闭了默认开启的UsePerfData参数(即使用参数-XX:-UsePerfData),那么 jps 命令就无法探知该 Java 进程。


当获得 Java 进程的进程 ID 之后,我们便可以调用接下来介绍的各项监控及诊断工具了。


除了任何必需 jstackjmapjinfojsnap 特定于模式的选项外,pidexecore 这三个选项适用于所有模式。


  • --pid
    挂起进程的进程ID。
  • --exe
    可执行文件名。
  • --core
    核心转储文件名。


关于 --exe 和 --core 的使用,暂时没有测试成功。


jmap


jmap(Memory Map for Java)命令用于生成堆转储快照(一般称为heapdump或dump文件)。如果不想使用 jmap 命令,可以配置程序启动时的 JVM 参数,比如说 -XX:+HeapDumpOnOutOfMemoryError 参数,可以在程序发生内存溢出异常后自动生成 dump 文件。通过 -XX:+HeapDumpOnCtrlBreak 参数则可以使用[Ctrl]+[Break]键让虚拟机生成堆转储快照文件,又或者在 Linux 系统下通过 Kill-3 命令发送进程退出信号“恐吓”一下虚拟机,也能顺利拿到堆转储快照。

JDK 自带的 jmap 命令格式如下:


jmap [option] [pid]
jmap -clstats <pid>   to connect to running process and print class loader statistics
jmap -finalizerinfo <pid> to connect to running process and print information on objects awaiting finalization
jmap -histo[:live] <pid>
  to connect to running process and print histogram of java object heap
  if the "live" suboption is specified, only count live objects
jmap -dump:<dump-options> <pid> to connect to running process and dump java heap
  dump-options:
    live         dump only live objects; if not specified,
    all objects in the heap are dumped.
    format=b     binary format
    file=<file>  dump heap to <file>
复制代码


可以看到 jmap 已经没有了-heap 命令参数,我们来看一下 jhsdb jmap,其中 options 位置不一定非要在后面。


jhsdb jmap [--pid pid | --exe executable --core coredump] [options]
// //其中 options 包括:
<no option> to print same info as Solaris pmap
--heap  to print java heap summary    //显示Java堆详细信息
--binaryheap  to dump java heap in hprof binary format
--dumpfile  name of the dump file //导出 Java 虚拟机堆的快照,生成文件
--histo to print histogram of java object heap  //打印 Java 对象堆的直方图
--clstats to print class loader statistics  //打印 Java 堆的类加载器统计信息
--finalizerinfo to print information on objects awaiting finalization //打印有关等待完成的对象的信息
复制代码


(不能再使用 jmap -heap pid 的命令了,需要使用上面的命令)。使用旧的命令会报错:


Error: -heap option used
Cannot connect to core dump or remote debug server. Use jhsdb jmap instead
复制代码


这里只演示两个简单示例,jmap -heap pid 展示 pid 的整体堆信息


//JDK9 
% jhsdb jmap --pid 2681 --heap
Attaching to process ID 2681, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 9.0.4+11
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 4294967296 (4096.0MB)
   NewSize                  = 10485760 (10.0MB)
   MaxNewSize               = 10485760 (10.0MB)
   OldSize                  = 257949696 (246.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)
Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 9437184 (9.0MB)
   used     = 1718600 (1.6389846801757812MB)
   free     = 7718584 (7.361015319824219MB)
   18.210940890842014% used
Eden Space:
   capacity = 8388608 (8.0MB)
   used     = 1718600 (1.6389846801757812MB)
   free     = 6670008 (6.361015319824219MB)
   20.487308502197266% used
From Space:
   capacity = 1048576 (1.0MB)
   used     = 0 (0.0MB)
   free     = 1048576 (1.0MB)
   0.0% used
To Space:
   capacity = 1048576 (1.0MB)
   used     = 0 (0.0MB)
   free     = 1048576 (1.0MB)
   0.0% used
tenured generation:
   capacity = 257949696 (246.0MB)
   used     = 0 (0.0MB)
   free     = 257949696 (246.0MB)
   0.0% used
2851 interned Strings occupying 192840 bytes.
复制代码


这一命令相较于我们在 clhsdb 中执行的 universe 命令更加详细,可以看到详细的内存分配。


jmap -histo pid 展示 class 的内存情况


说明:instances(实例数)、bytes(大小)、classs name(类名)。它基本是按照使用使用大小逆序排列的。


% jhsdb jmap --pid 2681 --histo
Attaching to process ID 2681, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 9.0.4+11
Iterating over heap. This may take a while...
Object Histogram:
num     #instances  #bytes  Class description
--------------------------------------------------------------------------
1:    3885  205144  byte[]
2:    3497  111904  java.util.HashMap$Node
3:    1038  91248 java.lang.Object[]
4:    3393  81432 java.lang.String
5:    687 74920 java.util.HashMap$Node[]
6:    601 72984 java.lang.Class
7:    1376  44032 java.util.concurrent.ConcurrentHashMap$Node
8:    780 37440 java.util.HashMap
9:    24  35968 char[]
//只输出前30的对象
% jhsdb jmap --histo --pid 2681 | head -n30
复制代码


关于该 jmap 的更多讲解,推荐阅读:java命令--jmap命令使用

关于 jmap 命令,使用最多的场景就是生成 dump 文件,其他命令的效果可有后续介绍到的监控工具来代替,比如说 VisualVM 等。下面是不同 JDK 环境导出 dump 文件的命令,不过需要注意的是,通过命令行方式执行时,JVM 是暂停服务的,所以对线上的运行会产生影响。不推荐该方式。


jhsdb jmap 命令也可以生成 dump 文件,下面两个命令都是可以的。


//JDK9
jhsdb jmap --binaryheap --dumpfile heap.hprof --pid 17714
jmap -dump:format=b,file=heap.hprof 17594
复制代码


在测试 JDK9 导出 dump 文件时遇到了个小问题,首先我是在 IDEA 中启动该程序,然后在命令行端口获取 pid,然后执行 jhsdb jmap 命令,结果报错了。


% jhsdb jmap --binaryheap --dumpfile heap.hprof --pid 17407
Attaching to process ID 17407, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 9.0.4+11
Exception in thread "main" sun.jvm.hotspot.types.WrongTypeException: No suitable match for type of address 0x000000032b938308
  at jdk.hotspot.agent/sun.jvm.hotspot.runtime.InstanceConstructor.newWrongTypeException(InstanceConstructor.java:62)
  at jdk.hotspot.agent/sun.jvm.hotspot.runtime.VirtualBaseConstructor.instantiateWrapperFor(VirtualBaseConstructor.java:109)
  at jdk.hotspot.agent/sun.jvm.hotspot.oops.Metadata.instantiateWrapperFor(Metadata.java:73)
  at jdk.hotspot.agent/sun.jvm.hotspot.oops.Oop.getKlassForOopHandle(Oop.java:211)
  at jdk.hotspot.agent/sun.jvm.hotspot.oops.ObjectHeap.newOop(ObjectHeap.java:252)
  at jdk.hotspot.agent/sun.jvm.hotspot.oops.ObjectHeap.iterateLiveRegions(ObjectHeap.java:331)
  at jdk.hotspot.agent/sun.jvm.hotspot.oops.ObjectHeap.iterate(ObjectHeap.java:172)
  at jdk.hotspot.agent/sun.jvm.hotspot.utilities.AbstractHeapGraphWriter.write(AbstractHeapGraphWriter.java:51)
  at jdk.hotspot.agent/sun.jvm.hotspot.utilities.HeapHprofBinWriter.write(HeapHprofBinWriter.java:433)
  at jdk.hotspot.agent/sun.jvm.hotspot.tools.JMap.writeHeapHprofBin(JMap.java:182)
  at jdk.hotspot.agent/sun.jvm.hotspot.tools.JMap.run(JMap.java:97)
  at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.startInternal(Tool.java:260)
  at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.start(Tool.java:223)
  at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.execute(Tool.java:118)
  at jdk.hotspot.agent/sun.jvm.hotspot.tools.JMap.main(JMap.java:176)
  at jdk.hotspot.agent/sun.jvm.hotspot.SALauncher.runJMAP(SALauncher.java:32
复制代码


关于错误原因,可以参考一下这篇文章,虽然错误不是完全一致,但是错误原因可以借鉴一下,初步认为是 IDEA 中程序运行时和命令行窗口中执行 jhsdb jmap 命令时,JVM 环境不一致。抱着试一试的态度,我在命令行窗口启动该程序,然后再次执行 jhsdb jmap 命令,成功生成 dump 文件。


% jhsdb jmap --binaryheap --dumpfile heap.hprof --pid 17714
Attaching to process ID 17714, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 9.0.4+11
heap written to heap.hprof
复制代码


jinfo


jinfo(Configuration Info for Java)的作用是实时查看和调整虚拟机各项参数。使用 jps 命令的-v参数可以查看虚拟机启动时显式指定的参数列表,但如果想知道未被显式指定的参数的系统默认值,除了去找资料外,就只能使用 jinfo 的-flag 选项进行查询了。


Usage:
    jinfo <option> <pid>
       (to connect to a running process)
where <option> is one of:
    -flag <name>         to print the value of the named VM flag
    -flag [+|-]<name>    to enable or disable the named VM flag
    -flag <name>=<value> to set the named VM flag to the given value
    -flags               to print VM flags
    -sysprops            to print Java system properties
    <no option>          to print both VM flags and system properties
    -h | -help           to print this help message
复制代码


比如可以使用 jinfo -flag +HeapDumpAfterFullGC 命令,开启所指定的 Java 进程的 HeapDumpAfterFullGC 参数。


再来看看 jhsdb jinfo 命令格式如下:


jhsdb jinfo [--pid pid | --exe executable --core coredump] [options]
//其中 options 包括:                   
    --flags to print VM flags //打印 VM 标志。
    --sysprops  to print Java System properties //打印 Java 系统属性
    <no option> to print both of the above  //打印 VM 标志和 Java 系统属性
复制代码

而 --flags 只能输出进程配置的 JVM 参数


% jhsdb jinfo --pid 17714 --flags
Attaching to process ID 17714, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 9.0.4+11
Non-default VM flags: -XX:CICompilerCount=4 -XX:ConcGCThreads=3 -XX:G1ConcRefinementThreads=10 -XX:G1HeapRegionSize=1048576 -XX:InitialHeapSize=268435456 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=4294967296 -XX:MaxNewSize=2576351232 -XX:MinHeapDeltaBytes=1048576 -XX:NonNMethodCodeHeapSize=5835340 -XX:NonProfiledCodeHeapSize=122911450 -XX:ProfiledCodeHeapSize=122911450 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:-UseAOT -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseG1GC 
Command line:
复制代码


jinfo --pid 用来查看目标 Java 进程的参数。


具体示例如下:

上一篇: Java jmap内存分析工具可能导致JVM崩渍:它会暂停线程执行吗?

下一篇: 实战技巧:优化JVM性能 - 通过jmap与jhat深入探究线上系统中的对象分布情况