陈同学
微服务
Accelerator
About
# 如何监控Java GC > 原文:[How to Monitor Java Garbage Collection](https://www.cubrid.org/blog/how-to-monitor-java-garbage-collection/) by Sangmin Lee ON 06/01/2017 > 译文:[码代码的陈同学](https://chenyongjun.vip/) 本文是 **成为Java GC专家** 系列的第二篇。在上篇 [理解 Java GC](https://chenyongjun.vip/articles/48) 中我们学习了不同GC算法的处理过程,GC是如何工作的,什么是年轻代和老年代,JDK7中的5种GC类型,以及每种GC类型对性能的影响。 在本文中,我将讲述JVM如何实时执行垃圾收集。 ## 什么是GC监控 GC监控指了解JVM是如何运行GC的过程,例如,我们可以了解: 1. 一个年轻代中的对象什么时候会被移动到老年代,这个过程会消耗多长时间 2. 或者说 什么时候会发生 [stop-the-world](https://www.cubrid.org/blog/understanding-java-garbage-collection/#stop-the-world) ,持续时间有多久 GC监控就是为了弄清JVM是否在进行高效的垃圾收集,并检查是否有必要进行GC优化。基于以上信息,我们可以修改应用程序的代码或者调整GC种类(GC优化)。 ## 如何监控GC 监控GC的方式有多种,唯一的区别是展示GC信息的方式不同。GC操作由JVM完成,由于GC监控工具获取的GC信息都是由JVM提供,所以无论使用何种工具监控GC都将得到相同的结果,因此你没必要学习所有的GC监控方式。不过由于了解每种GC监控方式只需少量的时间,多知道几种还是有助于你在不同场景和环境中选择合适的GC监控方式。 下面罗列的工具和JVM选项并非普适所有HVM服务商,因为GC信息的提供并没有统一的标准。本文的案例基于 **HotSpot JVM**(Oracle JVM)。 首先,根据访问接口的不同,GC监控方式可以分为**CUI(命令行交互接口)** 和 **GUI(图形界面接口)** 两种。典型的CUI GC监控方法包含了一个叫做 **jstat** 的独立CUI程序,或者在运行JVM时使用 **verbosegc** 选项。 GUI GC监控是通过独立的GUI程序来完成,使用最广泛的三种程序分别为:jsonsole、jvisualvm和Visual GC。 让我们了解下每种监控方式。 ### jstat > jstat: JVM Statistics Monitoring Tool;jstatd: jstat detail **jstat** 是HotSpot JVM中的一个监控工具,HotSpot JVM中其他的监控工具有 **jps** 和 **jstatd**。有时,你需要结合使用这三种方式来监控Java应用。 **jstat** 不仅可以提供GC操作信息,也可以提供class loader的操作信息或JIT编译器的操作信息。在jstat可以提供的所有信息中,我们将仅介绍监控GC操作信息的功能。 **jstat** 位于$JDK_HOME/bin下,所以如果 *java* 或 *javac* 可以运行,那么 jstat也可以直接使用。 你可以试试下面的命令. `jstat –gc $<vmid$> 1000` ![](https://blog-1256695615.cos.ap-shanghai.myqcloud.com/2018/07/02/473e0847275047b69642d9a1cddb1ca8.png) 上面例子中输出了如下列信息: **S0C S1C S0U S1U EC EU OC OU PC** **vmid**(Virtual Machine ID),顾名思义,指虚拟机的ID。本地或远程运行的Java应用都会有一个vmid,运行在本机的Java应用的vmid也叫 **lvmid**(Local vmid),或者叫PID,可以通过 **ps** 命令或者Windows的命令管理器来找到lvmid,不过建议使用 **jps** 命令来获取,因为PID和lvmid不一定匹配。**jps**表示Java PS,jps可以显示vmids和main方法信息,和ps命令显示进程的PID效果一样。 首先通过jps命令找到你要监控的Java应用的vmid,并把它作为jstat的参数。当几个WAS实例运行在同一台设备上时,如果你只使用jps命令,将只能看到启动(bootstrap)信息。建议在这种情况下使用 **ps -ef | grep java**与 **jps** 配合使用。 GC性能数据需要持续的监测,因此,当运行**jstat**时,需要以一定的频率输出GC监控信息。 例如,运行 " **jstat -gc <vmid> 1000** " (or 1s) 命令将在控制台每次刷新一次监控数据。 | Option Name | Description | | -------------- | ------------------------------------------------------------ | | gc | It shows the current size for each heap area and its current usage (Ede, survivor, old, etc.), total number of GC performed, and the accumulated time for GC operations. | | gccapactiy | It shows the minimum size (ms) and maximum size (mx) of each heap area, current size, and the number of GC performed for each area. (Does not show current usage and accumulated time for GC operations.) | | gccause | It shows the "information provided by -gcutil" + reason for the last GC and the reason for the current GC. | | gcnew | Shows the GC performance data for the new area. | | gcnewcapacity | Shows statistics for the size of new area. | | gcold | Shows the GC performance data for the old area. | | gcoldcapacity | Shows statistics for the size of old area. | | gcpermcapacity | Shows statistics for the permanent area. | | gcutil | Shows the usage for each heap area in percentage. Also shows the total number of GC performed and the accumulated time for GC operations. | 使用频率最高的按顺序可能是:**-gcutil**(或gccause)、**-gc** 和 **-gccapacity**。 * **-gcutil** 用于检查堆区域内存使用情况,GC次数以及GC操作的总时间 * **-gccapacity** 和其他选项用于检查实际分配的内存大小 不同的jstat选项会展示不同的列,以下是列信息。 | Column | Description | Jstat Option | | ------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | S0C | Displays the current size of Survivor0 area in KB | -gc -gccapacity -gcnew -gcnewcapacity | | S1C | Displays the current size of Survivor1 area in KB | -gc -gccapacity -gcnew -gcnewcapacity | | S0U | Displays the current usage of Survivor0 area in KB | -gc -gcnew | | S1U | Displays the current usage of Survivor1 area in KB | -gc -gcnew | | EC | Displays the current size of Eden area in KB | -gc -gccapacity -gcnew -gcnewcapacity | | EU | Displays the current usage of Eden area in KB | -gc -gcnew | | OC | Displays the current size of old area in KB | -gc -gccapacity -gcold -gcoldcapacity | | OU | Displays the current usage of old area in KB | -gc -gcold | | PC | Displays the current size of permanent area in KB | -gc -gccapacity -gcold -gcoldcapacity -gcpermcapacity | | PU | Displays the current usage of permanent area in KB | -gc -gcold | | YGC | The number of GC event occurred in young area | -gc -gccapacity -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause | | YGCT | The accumulated time for GC operations for Yong area | -gc -gcnew -gcutil -gccause | | FGC | The number of full GC event occurred | -gc -gccapacity -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause | | FGCT | The accumulated time for full GC operations | -gc -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause | | GCT | The total accumulated time for GC operations | -gc -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause | | NGCMN | The minimum size of new area in KB | -gccapacity -gcnewcapacity | | NGCMX | The maximum size of max area in KB | -gccapacity -gcnewcapacity | | NGC | The current size of new area in KB | -gccapacity -gcnewcapacity | | OGCMN | The minimum size of old area in KB | -gccapacity -gcoldcapacity | | OGCMX | The maximum size of old area in KB | -gccapacity -gcoldcapacity | | OGC | The current size of old area in KB | -gccapacity -gcoldcapacity | | PGCMN | The minimum size of permanent area in KB | -gccapacity -gcpermcapacity | | PGCMX | The maximum size of permanent area in KB | -gccapacity -gcpermcapacity | | PGC | The current size of permanent generation area in KB | -gccapacity -gcpermcapacity | | PC | The current size of permanent area in KB | -gccapacity -gcpermcapacity | | PU | The current usage of permanent area in KB | -gc -gcold | | LGCC | The cause for the last GC occurrence | -gccause | | GCC | The cause for the current GC occurrence | -gccause | | TT | Tenuring threshold. If copied this amount of times in young area (S0 ->S1, S1->S0), they are then moved to old area. | -gcnew | | MTT | Maximum Tenuring threshold. If copied this amount of times inside young arae, then they are moved to old area. | -gcnew | | DSS | Adequate size of survivor in KB | -gcnew | **jstat** 的好处是只要有控制台可以使用,就可以持续的监控本地或远程运行中Java应用的GC操作信息。当使用 **-gcutil** 时会输出以下结果,在进行GC优化时,要特别注意:YGC、YGCT、FGC、FGCT和GCT。 ![](https://blog-1256695615.cos.ap-shanghai.myqcloud.com/2018/07/02/d1befc8e15634ed981bb944a0d6edbbc.png) 这些指标非常重要,它们展示了GC运行的时间。 在这个例子中,YGC一共217次,YGCT为0.928秒,通过计算算数平均值,可以知道每次Yong GC的平均时间为4ms,同样可以算出 Full GC的平均时间为33ms. 但是算数平均值对于分析实际的GC问题可能并没有太大作用,这是因为GC之间的时间差异很大(换句话说,如果Full GC平均时间为0.067s,其中有一次可能是将近1ms,而另一次可能是将近57ms)。为了了解每次GC的时间,而不是使用算术平均数来代替,最好使用 **-verbosegc**。 ### -verbosegc **-verbosegc** 是在Java应用启动时指定的JVM选项。*jstat* 可以在不指定任何JVM参数的情况下使用,**-verbosegc** 需要在JVM启动时指定,因此这个选项可能认为没什么必要(因此可以使用jstat代替)。然而,当发生GC时 **-verbosegc** 展示的结果更加容易理解,对于监控GC操作也十分有用。 | | jstat | -verbosegc | | ------------ | ------------------------------------------------------------ | -------------------------------------------- | | 监控对象 | 本机Java应用可以将日志输出到终端,远程应用可以借助jstatd使用网络连接来进行监控 | 仅在JVM启动时设置了-verbogc参数才有效 | | 输出信息 | 堆状态信息(使用情况、最大size、GC次数、时间等) | GC执行前后新生代和老年代的大小以及GC操作时间 | | 输出时间 | 根据设定好的时间输出 | 任何发生GC的时候都会输出 | | 什么时候有用 | 尝试观察堆区空间变化时 | 尝试监控单次GC时 | 以下选项可以和 **-verbosegc** 结合使用: * -XX:+PrintGCDetails * -XX:+PrintGCTimeStamps * -XX:+PrintHeapAtGC * -XX:+PrintGCDateStamps (from JDK 6 update 4) 如果只使用了 **-verbosegc**,默认会加上 **-XX:+PrintGCDetails** 选项。其他选项也可以和 **-verbosegc** 结合使用。 当使用 **-verbosegc**时,当发生Minor GC时你将看到以下格式的结果: ``` [GC [<collector>: <starting occupancy1> -> <ending occupancy1>, <pause time1> secs] <starting occupancy3> -> <ending occupancy3>, <pause time3> secs] ``` | 收集器 | minor gc时收集器的名字 | | ------------------- | ------------------------------------------------------------ | | starting occupancy1 | The size of young area before GC | | ending occupancy1 | The size of young area after GC | | pause time1 | The time when the Java application stopped running for minor GC | | starting occupancy3 | The total size of heap area before GC | | ending occupancy3 | The total size of heap area after GC | | pause time3 | The time when the Java application stopped running for overall heap GC, including major GC | 下面是**-verbosegc** 输出minor GC的例子: ![](https://blog-1256695615.cos.ap-shanghai.myqcloud.com/2018/07/02/d1befc8e15634ed981bb944a0d6edbbc.png) 这是Full GC发生时的例子: ``` [Full GC [Tenured: 3485K->4095K(4096K), 0.1745373 secs] 61244K->7418K(63104K), [Perm : 10756K->10756K(12288K)], 0.1762129 secs] [Times: user=0.19 sys=0.00, real=0.19 secs] ``` 如果使用了 CMS collector,那么如下CMS信息也会被输出。 由于 –verbosegc 参数在每次GC事件发生的时候都会输出日志,我们可以很轻易地观察到GC操作对于堆空间的影响。 ### (Java) VisualVM + Visual GC Java Visual VM是 Oracle JDK提供的图形化的分析/监控工具。 ![](https://blog-1256695615.cos.ap-shanghai.myqcloud.com/2018/07/03/99db5f76dddc4e95a7165214240e15ad.png) 除了JDK中自带的版本,你还可以直接从官网下载Visual VM。出于便利性的考虑,JDK中包含的版本被命名为Java VisualVM (jvisualvm),而官网提供的版本被命名为Visual VM (visualvm)。两者的功能基本相同,只有一些细小的差别,例如安装组件的时候。就个人而言,我更喜欢可以从官网下载的Visual VM。 运行Visual VM之后,你可以从左边窗口选择要监控的应用。 你可以找到 "Monitor" tab页,从该页可以查看GC和堆的基本信息。 虽然通过Visual VM的基础功能可以查看GC基础的状态信息,但是无法像 **jstat** 和 **-verbosegc** 一样查看明细信息。 如果像查看由 jstat 提供的详细信息,推荐安装 Visual GC插件。 Visual GC插件可以通过 *Tools* 菜单实时访问。 ![](https://blog-1256695615.cos.ap-shanghai.myqcloud.com/2018/07/03/d2012d949b4041389843ce4b2e7c5677.png) 通过Visual GC,可以更直观的查看由 **jstatd** 提供的信息。 ![](https://blog-1256695615.cos.ap-shanghai.myqcloud.com/2018/07/03/6b49d00c86304d699aedde86522c8313.png) ### HPJMeter HPJMeter 可以很方便的分析 **-verbosegc** 输出的结果,如果说Visual GC是jstat图形化产品,那么HPJMeter就相当于 **–verbosgc**的图形化产品。当然,GC分析只是HPJMeter提供的众多功能之一,HPJMeter是一个由惠普开发的性能监控工具,它可以在HP-UX,Linux以及MS Windows中使用。 起初,一个叫 **HPTune** 的工具用于展示**-verbosegc**的输出结果。然而,随着HPTune的功能被集成到HPJMeter 3.0版本之后,就没有必要再单独下载HPTune。 运行一个应用时,** -verbosegc** 的结果会被输出到一个独立的文件中。 你可以用HPJMeter直接打开这个文件,通过GUI更快、更直观的分析GC性能数据。 ![](https://blog-1256695615.cos.ap-shanghai.myqcloud.com/2018/07/03/403c2d1a8e634aa086d3a717d0147988.png) ## 下一篇文章简介 **下次预告** 本文主要讲述了如何监控GC操作信息,这是GC优化的前提。就我个人经验而言,我推荐使用jstat来监控GC操作,如果你感觉到GC操作的执行时间过长,可以使用**-verbosegc**参数来分析GC。GC优化通用步骤是在添加**-verbosegc**参数后,再调整GC参数,分析修改后的结果。在下一篇文章中,将通过真实案例作为例子来讲解GC优化的最佳JVM选项。
本文由
cyj
创作,可自由转载、引用,但需署名作者且注明文章出处。
文章标题:
如何监控Java GC
文章链接:
https://chenyongjun.vip/articles/54
扫码或搜索 cyjrun 关注微信公众号, 结伴学习, 一起努力