写点什么

JVM 故障诊断和处理工具

用户头像
Silently9527
关注
发布于: 2021 年 01 月 14 日

本文已被 Github 仓库收录 https://github.com/silently9527/JavaCore

微信公众号:贝塔学 Java


前言

前几天中午正在和同事最近聊股市较好,这几天每天都可以喝点肉汤,心里还是挺高兴的;正在这个时候收到了线上告警邮件和运维同学的消息,“你们有服务挂了!”,心里一紧,立马打开电脑看来下线上 cat 监控大盘,发现很多服务都在报错,根据 cat 上的监控日志很快发现了其中一个服务内存溢出导致其他调用服务也有问题,竟然已经定位到了出问题的服务,那就简单了,没有是重启解决不了的问题,重启之后很快服务都恢复正常了。几分钟之后又报错了,同样也是这个服务内存溢出,经过排查后发现该服务的堆内存被改小了,好家伙,运维同学不讲武德,搞偷袭,趁我没反应过来调了内存,内存调整回去之后服务就恢复了正常。


事后把线上的快照文件拖了下来分析,发现本身这个项目的代码也有些问题,本文就整理了一下 JVM 常用的分析工具。


命令行工具

在安装完 JDK 之后在 JAVA_HOME/bin 目录下 JDK 已经提供了很多命令行的工具



可能我们最常用的就是javajavac这两个命令,除了这两个命令之外还有提供很多其他的实用工具,本文主要来一起学习对 JVM 监控诊断工具


虚拟机进程状况工具(jps)

该工具的功能比较单一,与 linux 中的 ps 功能类似,用来列出正在运行的虚拟机进程,并显示出运行的主类和进程号


命令格式:jps [option] [hostid]


> 如果需要查看远程机器的 jvm 进程需要填写hostid,并且需要使用 RMI,比如:rmi://192.168.2.128:12345


常用的选项:

  • -q : 只显示出虚拟机的进程 id(lvmid),省略主类名

  • -m : 输出启动时传递给主类的参数

  • -l : 显示出主类的全名,包括 jar 包路径

  • -v : 输出虚拟机进程启动时的 JVM 参数



虚拟机统计信息监控工具(jstat)

用于监控虚拟机运行状态信息的命令行工具,可以提供内存,垃圾收集等云行时的数据


命令格式:jstat [option vmid] [interval [s|ms] [count]]


interval 表示间隔多久时间查询一次,count 表示查询多少次,比如:每个两秒查询一次进程 52412 的垃圾收集情况,共查询 5 次


jstat -gc 52412 2s 5
复制代码



常用的选项:

  • -class: 监控类装载,卸载次数和总空间以及加载类的耗时

  • -gc: 监控 java 堆的情况

  • -gcutil: 主要输出各个空间使用的百分比

  • -gcnew: 主要是监控新生代的 GC 状况

  • -gcold: 监控老年代的 GC 状况

  • -compiler: 输出 JIT 编译器编译过的方法和耗时信息


查看堆空间的使用百分比: jstat -gcutil 52412 2s 5



java 配置信息工具(jinfo)

可以通过jinfo实时的查看和调整虚拟机的各项参数;可以通过jps -v查看虚拟机启动时候指定的参数信息,如果需要查看未显示指定的参数默认值也可以通过jinfo -flag


jinfo -flag CMSInitiatingOccupancyFraction 52412
复制代码



jinfo 除了可以查看参数以外,还可以在运行时修改一些允许被修改的参数


Java 内存映像工具(jmap)

jmap 用于生成 JVM 堆的快照文件,除了使用 jmap 工具,我们通常也会在配置 JVM 的启动参数 -XX:+HeapDumpOnOutOfMemoryError 让 JVM 在发送内存溢出之后自动生成 dump 文件。


命令格式:jmap [option] vmid


比如生成 java 堆的快照文件


jmap -dump:live,format=b,file=/Users/huaan9527/Desktop/heap.hprof 59950
复制代码


常用的选项:

  • -F: 当虚拟机对-dump 选项没有响应时可用选择使用这个参数强制生成快照

  • -histo: 显示出堆中对象统计信息。


堆栈跟踪工具(jstack)

用于生成 JVM 当前线程的快照信息。通常用于查询什么原因导致线程长时间的停顿,比如:线程死循环,死锁,等待网络/IO


命令格式:jstack [option] vmid


常用的选项:

  • -F: 当请求不被响应时强制输出

  • -l: 除了显示堆栈外,还需要显示锁的信息

  • -m: 如果调用到本地方法,显示出 C/C++的堆栈


VisualVM 可视化工具

VisualVM 是目前 JDK 自带的功能最强的运行监视和故障处理程序,在 VisualVM 之前,JDK 也提供了一款可视化工具 JConsole,由于 JConsole 的所有功能在 VisualVM 都有,所以可视化工具大家几乎都选择使用 VisualVM。


VisualVM 本身是基于 Netbean 开发的,所以具备了插件扩展功能,安装插件之后上面介绍的所有命令行的工具的功能都可以在 VisualVM 中使用。可以在在 JAVA_HOME/bin 目录下执行jvisualvm启动。


  • 插件安装

默认情况 VisualVM 提供的功能很少,需要我们在菜单栏->工具->插件里面安装插件,我这是全部插件都安装了



功能演示

  • 应用程序、概述、监视



显示出当前本机所有的 JVM 进程,这里显示的内容和前面说的命令行jps显示的内容一样



当前虚拟机启动信息的展示,比如:JVM 启动参数、系统参数



这个页面相当于命令 jstat 的功能,显示出了 CPU, 内存,线程,类装载当前处于什么情况


生成 dump 文件可以在应用程序窗口右键菜单中选择,也可以在这个页面点击右上角的堆dump


  • Visual GC

此页主要展示了 GC 相关的信息,这是在性能调优时常用的页面之一



我们可以写个程序来观看下这个截图各个内存区域的变化情况,为了让图的效果明显需要修改 JVM 的启动参数


-Xmx100m -Xms100m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/huaan9527/Desktop
复制代码


public static void main(String[] args) {    List<datatest> datas = new ArrayList&lt;&gt;();
IntStream.range(0, 10000).forEach(index -&gt; { datas.add(new DataTest());
try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } });
System.gc();}
static class DataTest { byte[] bytes = new byte[1024];}
复制代码



  • 线程

本页的功能相当于命令行工具jstack,主要是用于检查什么原因导致线程长时间等待,我们写程序来演示下等待外部资源、锁等待、死循环这几种请求


等待外部资源


public static void main(String[] args) throws IOException {    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));    System.out.println(reader.readLine());    try {        Thread.sleep(1000000);    } catch (InterruptedException e) {        e.printStackTrace();    }}
复制代码

选中 main 线程,右侧会看到当前线程运行到了 readBytes,等待键盘输入



当我们在控制台输入之后再次查看 main 线程的状态,此时进入了 TIME_WAIT 状态



锁等待


public static void main(String[] args) throws IOException, InterruptedException {    Thread thread = createLockThread(new Object());    thread.join();}
public static Thread createLockThread(final Object lock) { Thread lockThread = new Thread(() -&gt; { synchronized (lock) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "lockThread"); lockThread.start(); return lockThread;}
复制代码



lockThread 线程在等待 lock 对象的 notify 方法被调用,此时处于 WAITING 状态,在被唤醒之前是不会再分配执行时间


死循环


public static void main(String[] args) throws IOException, InterruptedException {    while (true) ;}
复制代码



线程一直处于运行状态,从堆栈追踪里可以看出代码一直停留在了 191 行,在空循环上用尽分配的执行时间


总结

本篇介绍了命令行工具和可视化工具,下篇实战演示下如何通过这些工具对 Idea 运行速度调优




写到最后(点关注,不迷路)

文中或许会存在或多或少的不足、错误之处,有建议或者意见也非常欢迎大家在评论交流。


最后,白嫖不好,创作不易,希望朋友们可以点赞评论关注三连,因为这些就是我分享的全部动力来源🙏


原创不易 转载请注明出处:https://silently9527.cn/archives/100


发布于: 2021 年 01 月 14 日阅读数: 22
用户头像

Silently9527

关注

公众号:贝塔学JAVA 2018.05.09 加入

Simple Programmer, Make the complex simple

评论

发布
暂无评论
JVM故障诊断和处理工具