写点什么

JDK 中自带的 JVM 分析工具

作者:知了一笑
  • 2022-10-25
    浙江
  • 本文字数:3251 字

    阅读完需:约 11 分钟

JDK中自带的JVM分析工具

内存溢出,妥妥的名场面;

一、业务背景

对于分布式架构中的文件服务来说,由于涉及大量的 IO 流操作,很容易引发 JVM 的相关异常,尤其是内存溢出的问题;



在最近的一次版本迭代中,真实的业务处理场景和上述几乎一致,由于在文件服务中添加批量处理的动作,直接唤醒了隐藏许久的 BUG,就是最常见的内存溢出;


问题的起因:在 word 文档完成内容识别后,转换为 pdf 文件,然后进行页面分割转为一组图片,在这个复杂并且超长的流程中存在一个数组容器未销毁;


解决的方式:分析 JVM 的 dump 文件,定位 OOM 问题引发的根本原因,结合文件服务的异常日志分析,添加资源的释放动作,从而解决问题;

二、Jdk-Bin 目录

对于相当一部分新手来说,看到 JVM 的问题都是 Bug 不知所起一脸懵的,其实这种心态大可不必,从职场几年的开发经验上看,JVM 的问题大致分为两种:


  • 开发轻松解决:可以升级内存资源或者调整分配,又或者对程序优化,完成相关资源的管理和释放,这是最常用的手段;

  • 轻松解决开发:由于经验不足,程序出现重大 BUG 导致 JVM 异常,进而引起系列的连锁反应,这种不会绝地反弹,只有一地鸡毛;


在解决常规的 JVM 异常时,通常依赖 JDK 中基础工具即可完成问题的定位,从而进行分析和解决,不过这些需要对基础工具熟练使用才行,而很多 JDK 自身的能力又是经常被忽略的;


在 jdk 的 bin 目录中,有很多自带工具可以用于对 JVM 的分析;



上述是基于 jdk1.8 的目录,里面有很多开发经常用到命令,下面围绕一个微服务的启动和运行,来看看基于 JDK 中自带 JVM 工具的用法;

三、命令行工具

1、jps 命令

jps:虚拟机进程状态工具,该命令在 Java 环境部署和服务启动查看时经常用到,首先在本地启动一个 facade 门面微服务,然后在命令行中执行查询;


  • jps:命令默认输出的是进程 ID 和应用主类的名称;

  • -l:输出进程 ID 和应用主类的完整路径;

  • -v:输出向 jvm 传递的参数,此处展示为 idea 中显式配置的 VM-options 参数,其他内容自行查看即可;

  • -m:输出向 main 方法传递的参数,服务启动前可以在 idea 的 Program-arguments 配置;


$ jps1281 FacadeApp
$ jps -l1281 com.explore.facade.FacadeApp
$ jps -v1281 FacadeApp -Xms128m -Xmx256m -XX:MaxNewSize=256m -XX:MaxPermSize=256m
$ jps -m1281 FacadeApp hello,main-method
复制代码

2、jinfo 命令

jinfo:在命令后面带 pid 进程号,可以输出指定进程的配置信息,在应用启动时通常不会指定过多的配置参数,就可以使用该命令查询很多参数的默认值;该命令还可以在运行时动态调整部分参数,只是很少被使用;


$ jinfo 1281            # 只粘贴个别参数Java System Properties: # 系统参数    java.runtime.version=1.8.0_144-b01    file.encoding=UTF-8    sun.java.command=com.explore.facade.FacadeApp hello,main-method    VM Flags:               # 虚拟机参数    -XX:InitialHeapSize=134217728 -XX:MaxHeapSize=268435456 -XX:MaxNewSize=267911168    VM Arguments:           # 运行时参数    jvm_args: -Xms128m -Xmx256m -XX:MaxNewSize=256m -XX:MaxPermSize=256m    java_command: com.explore.facade.FacadeApp hello,main-method
$ jinfo -sysprops 1281 # 只输出【Java System Properties】参数
$ jinfo -flags 1281 # 只输出【VM Flags】参数
复制代码

3、jstat 命令

jstat:以指定的频率输出 JVM 的监控指标,下述命令输出内存占用和 GC 相关信息,每隔 3 秒输出一次,连续打印 5 次;由于这里只是启动一个简单的微服务,没有执行业务逻辑,所以各项指标比较平稳;


$ jstat -gcutil 1281 3000 5S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   0.00   0.00  57.97  64.16  92.82  88.75    3    0.028     9    0.516     -        -    0.5440.00   0.00  57.97  64.16  92.82  88.75    3    0.028     9    0.516     -        -    0.544
复制代码


该命令是比较常用的,这里各项指标的统计逻辑,在tools.jar包中有jstat_options参考文档,相对路径sun/tools/jstat/resources/目录下;


option gcutil {  column {    header "^S0^"  /* Survivor 0 Space - Percent Used */    data (1-((sun.gc.generation.0.space.1.capacity - sun.gc.generation.0.space.1.used)/sun.gc.generation.0.space.1.capacity)) * 100  }  column {    header "^S1^"  /* Survivor 1 Space - Percent Used */    data (1-((sun.gc.generation.0.space.2.capacity - sun.gc.generation.0.space.2.used)/sun.gc.generation.0.space.2.capacity)) * 100  }  ......}
复制代码

4、jstack 命令

jstack:输出指定进程当前时刻在 JVM 中的线程信息,为了清楚的展示其效果,在服务启动时创建线程死锁,然后通过该命令就会把发生死锁的线程打印出来,通过输出可以发现两条互相等待的线程信息;


$ jstack 1281Found one Java-level deadlock:============================="test-thread-02":  waiting for ownable synchronizer 0x00000007b00a35d0, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),  which is held by "test-thread-01""test-thread-01":  waiting for ownable synchronizer 0x00000007b00a35a0, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),  which is held by "test-thread-02"
Java stack information for the threads listed above:==================================================="test-thread-02": at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000007b00a35d0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
"test-thread-01": at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000007b00a35a0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
Found 1 deadlock.
复制代码

5、jmap 命令

jmap:可以输出指定进程的内存中对象映射信息,或者堆的关键信息、内存的使用统计、GC 算法、配置、类的实例信息及内存占用等,该命令在解决 JVM 问题时也经常使用;


$ jmap 1281
$ jmap -heap 1281Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 536870912 (512.0MB) Heap Usage: PS Young Generation Eden Space: From Space: To Space: PS Old Generation
$ jmap -histo:live 1281 num #instances #bytes class name----------------------------------------------1311: 1 32 com.explore.facade.FacadeApp$$EnhancerBySpringCGLIB$$313d9e3
复制代码

四、可视化工具

1、jconsole

Java 内置的 JVM 性能监控工具,在熟悉上述的命令行工具之后,对于该可视化工具的使用不会太陌生,在命令中可以查看到的默认参数或者应用自定义配置,在该工具中也可以找到,并且以图形化的方式呈现;


$ jconsole  # 通过该命令会唤起jconsole界面
复制代码



这里选择线程一栏,可以直观的看到线程数量的变化曲线,也可以在下方查看某个线程的具体信息,并且可以通过检测死锁功能,发现在服务中创建的 test-thread-01 和 test-thread-02 两条线程;


2、visualvm

VisualVM 作为解决 JVM 问题的常用工具,集成的功能丰富且强大,此处通过 Idea 插件的方式启动 FacadeApp 微服务,在进程主页可以看到自定义的配置,线程一栏因为检测到死锁直接给到异常提示;



在监视一栏可以通过【堆 dump】查看详细的信息,可以查看类的实例数和大小,并且完成了结果排序和占用统计;此处信息在定位和解决 JVM 问题时非常重要;



对于 JVM 的监控工具来说,其能力与常用的命令行语法差异很小,并且这些命令在 jdk 中tools.jar包也可以找到其对应的类,对于一些更高级的监控平台来说,例如 Kuboard、Skywalking 等,也对这些底层能力做了集成,其原理应该也是大同小异。

五、参考源码

编程文档:https://gitee.com/cicadasmile/butte-java-note
应用仓库:https://gitee.com/cicadasmile/butte-flyer-parent
复制代码


发布于: 刚刚阅读数: 5
用户头像

知了一笑

关注

公众号:知了一笑 2020-04-08 加入

源码仓库:https://gitee.com/cicadasmile

评论

发布
暂无评论
JDK中自带的JVM分析工具_Java_知了一笑_InfoQ写作社区