Java GC专家系列2:Java 垃圾回收的监控
这是”成为GC专家系列”文章的第二篇。在第一篇理解Java垃圾回收中我们学习了几种不同的GC算法的处理过程,GC的工作方式,新生代与老年代的区别。到目前为止,你应该已经了解了JDK 7中的5种GC类型,以及每种GC对性能的影响。
在本篇中,我将介绍JVM在真实环境中如何运行GC的。
什么是GC监控
GC监控 指的是在运行时跟踪JVM运行GC的过程。例如,通过GC监控,我们能找出:
- 何时新生代的对象会被移动到老年代,有多少对象被移到了老年代。
- 何时stop-the-world发生以及持续时间。
通过GC监控,能发现JVM是否在有效的运行GC以及是否需要额外的GC调优。基于这些信息,我们可以通过优化应用或者改变GC运行方式(GC调优),从而提高应用性能。
如何做GC监控
GC监控的方式很多,区别在于GC操作信息的展示会有所不同。GC是由JVM触发,因为GC监控工具展示的信息都是由JVM提供,所以不管使用哪种方式做GC监控,最终获取的信息都是一致的。因此,没有必要深入学习每种GC监控工具,只需要花些时间学习每种工具的使用方法,能够在不同的场合选择合适的工具即可。
因为JVM规范没有要求暴露GC信息的标准方法,所以下面列出的工具或JVM选项并不能适用于所有不同的JVM实现。在下面的介绍中都是基于Hotspot JVM(Oracle JVM)进行。因为NHN使用的是Oracle(Sun) JVM,所以在使用以下工具或JVM选项时并不会太困难。
首先,GC监控工具根据访问接口和方式不同分为CUI和GUI。经典的CUI 工具可以使用一个单独的CUI应用jstat,也可以在运行JVM时通过提供”-verbosegc“选项来实现。
GUI GC监控工具通过单独的GUI应用来实现,后面会介绍三个常用的GUI GC工具:jconsole, jvisualvm和Visual GC。
下面开始学习每一种GC监控方法:
jstat
jstat是Hotspot JVM内置的监控工具。Hotspot JVM还内置了其他监控工具如jps和jstatd。有时候需要这三种工具一起来监控Java应用的运行。
jstat 不只提供GC操作的相关信息,也还提供类加载和即时编译器相关的操作信息。尽管如此,本文我们只会涉及jstat提供的GC操作相关的功能。
jstat 位于$JDK_HOME/bin目录,如果java或javac命令能够正常运行,jstat命令也应该能够运行。
你可以在命令行中尝试一下:
$> jstat –gc $<vmid$> 1000 S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT 3008.0 3072.0 0.0 1511.1 343360.0 46383.0 699072.0 283690.2 75392.0 41064.3 2540 18.454 4 1.133 19.588 3008.0 3072.0 0.0 1511.1 343360.0 47530.9 699072.0 283690.2 75392.0 41064.3 2540 18.454 4 1.133 19.588 3008.0 3072.0 0.0 1511.1 343360.0 47793.0 699072.0 283690.2 75392.0 41064.3 2540 18.454 4 1.133 19.588 $>
如上所示,真实的内存各部分数据情况按以下各列顺序列出:
SOC S1C S0U S1U EC EU OU PC
vmid(虚拟机id: Virtual Machine ID),见名示意,表示VM的ID。运行在本地或远程的虚拟机都可以通过vmid指定。运行在本地虚拟机上的Java应用的vmid又称为lvmid(Local vmid),通常与PID相同。虽然可以通过ps命令或Windows的任务管理器查看PID的值从而得到lvmid,但更推荐使用jps,因为PID和lvmid之间并不总是一一对应。jps表示Java PS。正如ps命令可以看到PIDs和进程名,通过jps可以看到vmids和main方法信息。
通过jps找到你要监控的Java应用的vmid,然后作为jstat的参数即可。如果多个WAS实例运行在同一设备上时,如果只使用jps命令只能找到引导程序的信息。这时候就要ps -ef | grep java命令和jps命令一起使用。
GC性能数据需要持续观察,因此在运行jstat时需要定时输出GC的监控信息。
举例来说:运行jstat -gc <vmid> 1000(or 1s)将会每隔1s在控制台上输出一次GC数据。jstat -gc <vmid> 1000 10将会每隔1s输出一次GC数据,总共输出10次。
与GC相关的选项除了-gc,还有其他一些,如下表所示:
| 选项名称 | 描述 |
|---|---|
| gc | 输出堆空间上各分区当前的大小及使用量(Ede, Survivor, Old等),GC执行的总次数以及累积消耗的执行时长。 |
| gccapacity | 输出堆空间上各分区的最小和最大容量,当前大小,每个区上的GC执行次数(不输出当前使用量和累积的GC耗时)。 |
| gccause | 除了输出 -gcutil提供的信息外,还会输出最后一次GC和当前GC的原因。 |
| gcnew | 新生代上的GC性能数据。 |
| gcnewcapacity | 新生代容量的统计信息。 |
| gcold | 老年代的GC性能数据。 |
| gcoldcapacity | 老年代容量的统计信息。 |
| gcpermcapacity | 持久代(方法区)上的统计信息。 |
| gcutil | 以%的格式输出每个分区的使用量。同时也会输出GC执行的总次数及累积耗时。 |
如果只关心GC频率,通常使用-gcutil(或者 -gccause), -gc, -gccapacity即可。
- -gcutil 用于检测各区上的使用量,GC执行次数以及累积耗时,
- -gccapacity 和其他的几个选项可用于输出实际已分配的内存大小。
使用-gc选项的输出如下:
S0C S1C … GCT 1248.0 896.0 … 1.246 1248.0 896.0 … 1.246 … … … …
给jstat指定不同的选项会列出不同的列,如下列所示。表格右侧列出了会输出此信息的jstat选项。
| 数据列 | 描述 | 支持的jstat 选项 |
|---|---|---|
| S0C | Survivor0的当前容量 | -gc -gccapacity -gcnew -gcnewcapacity |
| S1C | S1的当前容量 | -gc -gccapacity -gcnew -gcnewcapacity |
| S0U | S0的使用量 | -gc -gcnew |
| S1U | S1的使用量 | -gc -gcnew |
| EC | Eden区的当前容量 | -gc -gccapacity -gcnew -gcnewcapacity |
| EU | Eden区的使用量 | -gc -gcnew |
| OC | old区的当前容量 | -gc -gccapacity -gcnew -gcnewcapacity |
| OU | old区的使用量 | -gc -gcnew |
| PC | 方法区的当前容量 | -gc -gccapacity -gcold -gcoldcapacity -gcpermcapacity |
| PU | 方法区的使用量 | -gc -gcold |
| YGC | Young GC次数 | -gc -gccapacity -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause |
| YGCT | Young GC累积耗时 | -gc -gcnew -gcutil -gccause |
| FGC | Full GC次数 | -gc -gccapacity -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause |
| FGCT | Full GC累积耗时 | -gc -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause |
| GCT | GC总的累积耗时 | -gc -gcold -gcoldcapacity -gccapacity -gcpermcapacity -gcutil -gccause |
| NGCMN | 新生代最小容量 | -gccapacity -gcnewcapacity |
| NGCMX | 新生代最大容量 | -gccapacity -gcnewcapacity |
| NGC | 新生代当前容量 | -gccapacity -gcnewcapacity |
| OGCMN | 老年代最小容量 | -gccapacity -gcoldcapacity |
| OGCMX | 老年代最大容量 | -gccapacity -gcoldcapacity |
| OGC | 老年代当前容量 | -gccapacity -gcoldcapacity |
| PGCMN | 方法区最小容量 | -gccapacity -gcpermcapacity |
| PGCMX | 方法区最大容量 | -gccapacity -gcpermcapacity |
| PGC | 方法区当前容量 | -gccapacity -gcpermcapacity |
| PC | 方法区的当前容量 | -gccapacity -gcpermcapacity |
| PU | 方法区使用量 | -gccapacity -gcold |
| LGCC | 上一次GC发生的原因 | -gccause |
| GCC | 当前GC发生的原因 | -gccause |
| TT | 存活阀值,如果对象在新生代移动次数超过此阀值,则会被移到老年代 | -gcnew |
| MTT | 最大存活阀值,如果对象在新生代移动次数超过此阀值,则会被移到老年代 | -gcnew |
| DSS | survivor区的理想容量 | -gcnew |
表格中容量数量单位为:KB
jstat的优点在于不管是本地还是远程Java应用,你都可以通过jstat命令查看GC操作相关的数据,并通过控制台输出这些信息。在使用-gcutil选项时,会输出如下字段的信息。在做GC调优时,尤其要关注YGC, YGCT, FGC, FGCT和GCT的数据变化。
S0 S1 E O P YGC YGCT FGC FGCT GCT 0.00 66.44 54.12 10.58 86.63 217 0.928 2 0.067 0.995 0.00 66.44 54.12 10.58 86.63 217 0.928 2 0.067 0.995 0.00 66.44 54.12 10.58 86.63 217 0.928 2 0.067 0.995
这些信息非常重要,它统计了GC运行时的耗时情况,能反映出GC的性能指标。
在上例中,YGC是217次, YGCT为0.928,平均下来每次young GC耗时4ms(0.004 s)。同样可算出full GC的平均耗时为33ms。
然而平均值对发现实现的GC问题并没有太大的帮助,因为每次GC耗时通常会有巨大的偏差(也就是说,如果full GC的平均值为0.067s,可能意味着其中一次GC耗时1ms,而另外一次持续134ms)。为了能观察每次GC的独立耗时而非平均值,更好的方式是使用-verbosegc。
-verbosegc
-verbosegc 是运行Java应用时的一个JVM选项。jstat可以监控任何JVM应用而无需指定启动参数,-verbosegc去要在开启应用时就指定好,所以看起来-verbosegc并不是一个必要的选项(因为可以使用jstat完成相同工作)。然而当GC发生时-verbosegc的输出信息更容易理解,这对于监控烦杂的GC信息却大于益处。
| jstat | -verbosegc | |
|---|---|---|
| 监控目标 | 可输出日志到终端上的Java应用或者能通过jstatd连接到网络的远程Java应用 | 在启动JVM时指定了-verbosegc 参数的Java应用 |
| 输出信息 | 堆状态(使用量、最大容量、GC次数及累积耗时等) | 每次GC前后新生代和老年代的容量变化及GC耗时 |
| 输出时机 | 任何指定的时间 | 任何GC发生时 |
| 优势 | 方便连续观察堆大小的变化 | 观察单次GC对系统的影响 |
在使用-verbosegc时还可同时指定以下附加选项:
- -XX:+PrintGCDetails
- -XX:+PrintGCTimeStamps
- -XX:+PrintHeapAtGC
- -XX:+PrintGCDateStamps(JDK6U4引入的选项)
如果只是指定了-verbosegc选项,则默认会同时指定-XX:+PrintGCDetails。另外,-verbosegc的附加选项都可以组合使用。
使用-verbosegc后,当有minor GC发生时,输出的数据格式如下:
[GC [<collector>: <starting occupancy1> -> <ending occupancy1>, <pause time1> secs] <starting occupancy3> -> <ending occupancy3>, <pause time3> secs]
| 字段 | 含义 | |
|---|---|---|
| Collector | 使用的收集器 | |
| starting occupancy1 | GC发生前的新生代大小 | |
| ending occupancy1 | GC后新生代的大小 | |
| pause time1 | 执行minor GC时Java应用停顿的时长 | |
| starting occupancy3 | GC发生前堆空间总大小 | |
| ending occupancy3 | GC发生后堆空间总大小 | |
| pause time3 | 执行总体GC(包括Full GC)时Java应用停顿时长 | |
下面是一个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回收算法](),CMS相关信息也会紧接着提供出来。
因为-verbosegc选项可以把每次GC发生时的信息都以log方式输出,所以很容易观察GC操作关后heap使用率的变化情况。
(Java) VisualVM + Visual GC
Java Virsual VM是Oracle JDK提供的一个GUI式的图表/监控工具。

图1:VirsualVM 界面
与内置在JDK中的版本不同,你可以在网站上单独下载Virsual VM。方便起见,JDK内置的版本称为Java VirsualVM(jvisualvm),从网站上单独下载的称为Virsual VM(visualvm)。二者之间的特性并不完全一致,在一些方面(例如安装插件等)会有细微的差别。就我个人而言,更偏向于使用单独下载的Virsual VM。
启动Visual VM后,如果你左侧面板上选择了希望监控的应用,就会看到”Monitoring”一栏。从Monitoring栏中可以获得关于GC和内存堆的基本信息。
尽管能通过Visual VM的基本特性得到GC的基本状态,但并不能像使用jstat和-verbosegc一样获得更详细的信息。
如果想得到像jstat一样的详细信息,则需要安装相应的Virsual VM插件。可以在Tools菜单里获取Virsual GC插件。

图2:Virsual GC安装界面
通过Virsual GC,可以以更直观的方式获得jstatd提供的信息。

图3:Virsual GC运行界面
HPJMeter
HPJMeter是一个分析-verbosegc输出结果的便捷工具。如果把Visual GC看作是jstat的GUI版本,那么HPJMeter则是-verbosegc的GUI版本。话说回来,GC分析只是HPJMeter提供的众多特性之一。HPJMeter是HP公司开发的一款性能监控工具,可以使用在HP-UX,Linux和MS Windows上。
起初,只是一款叫做HPTune的工具提供GUI的方式分析-verbosegc。自从HPJMeter 3.0开始便集成了HPTune,因此无需再单独下载HPTune。
在应用运行过程中,-verbosegc的输出结果可以重定向到一个单独的文件中。
可以通过HPJMeter打开该文件,然后使用直观的GUI界面便捷的分析GC数据。

图4:HPJMeter
下章介绍
本章作为GC调优的铺垫,着重于介绍了如何进行GC信息监控。一般情况我比较建议先使用jstat观察GC操作,当发现有比较耗时的GC后,再通过-verbosegc来分析GC数据。因此GC调优的一般过程就是分析和对比使用不同GC选项后-verbosegc输出结果的变化。下章将会通过真实案例来介绍进行GC调优的最佳选项。
作者:Sangmin Lee, 性能实验室高级工程师,NHN公司
Java GC专家系列2:Java 垃圾回收的监控的更多相关文章
- Java GC专家系列1:理解Java垃圾回收
了解Java的垃圾回收(GC)原理能给我们带来什么好处?对于软件工程师来说,满足技术好奇心可算是一个,但重要的是理解GC能帮忙我们更好的编写Java应用程序. 上面是我个人的主观的看法,但我相信熟练掌 ...
- Java GC专家系列4:Apache的MaxClients设置及其对Tomcat Full GC的影响
本文是GC专家系列中的第四篇.在第一篇理解Java垃圾回收中我们学习了几种不同的GC算法的处理过程,GC的工作方式,新生代与老年代的区别.所以,你应该已经了解了JDK 7中的5种GC类型,以及每种GC ...
- Java GC 专家系列5:Java应用性能优化的原则
本文是GC专家系列中的第五篇.在第一篇理解Java垃圾回收中我们学习了几种不同的GC算法的处理过程,GC的工作方式,新生代与老年代的区别.所以,你应该已经了解了JDK 7中的5种GC类型,以及每种GC ...
- Java GC 专家系列3:GC调优实践
本篇是”GC专家系列“的第三篇.在第一篇理解Java垃圾回收中我们学习了几种不同的GC算法的处理过程,GC的工作方式,新生代与老年代的区别.所以,你应该已经了解了JDK 7中的5种GC类型,以及每种G ...
- 成为JAVA GC专家系列
http://www.360doc.com/content/13/0305/10/15643_269387617.shtmlhttp://www.360doc.com/content/13/0305/ ...
- [译]GC专家系列2:Java 垃圾回收的监控
原文链接:http://www.cubrid.org/blog/dev-platform/how-to-monitor-java-garbage-collection/ 这是"成为GC专家系 ...
- [译]GC专家系列1: 理解Java垃圾回收
原文链接:http://www.cubrid.org/blog/dev-platform/understanding-java-garbage-collection/ 了解Java的垃圾回收(GC)原 ...
- (转)《深入理解java虚拟机》学习笔记3——垃圾回收算法
Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...
- 在Java中谈尾递归--尾递归和垃圾回收的比较(转载)
我不是故意在JAVA中谈尾递归的,因为在JAVA中谈尾递归真的是要绕好几个弯,只是我确实只有JAVA学得比较好,虽然确实C是在学校学过还考了90+,真学得没自学的JAVA好 不过也是因为要绕几个弯,所 ...
随机推荐
- mysql 修改root密码多种方法
方法1: 用SET PASSWORD命令 mysql -u root mysql> SET PASSWORD FOR 'root'@'localhost' = PASSWORD('newpa ...
- 【HDU1402】【FNT版】A * B Problem Plus
Problem Description Calculate A * B. Input Each line will contain two integers A and B. Process to ...
- ES的安装运行
一.安装,运行 1. 检查java的版本环境 Elasticsearch依赖Java,在书写本文档的时候,推荐使用Oracle JDK 1.8.0_20 或 1.7.0_55以后的版本. 在安装Ela ...
- PHP的类自动加载机制
在PHP开发过程中,如果希望从外部引入一个class,通常会使用include和require方法,去把定义这个class的文件包含进来. 这个在小规模开发的时候,没什么大问题.但在大型的开发项目中, ...
- Sql Server2000,2005,2008各版本主要区别
Emerson回来之后,在过程中遇到的一些问题,再次做一些整理,包括本篇的Sql Server各版本之间的区别和另一篇数据库函数. (博文内容来自网络) 数据类型 SQL Server 2008 数据 ...
- python随机数
前提:需要导入random模块 >>>import random 1.random.random random.random()用于生成一个0到1的随机符小数: 0 <= n ...
- 不同优化选项对ARM下C语言编译的影响
我们知道在C语言编译时,有那么几个常用的优化编译选项,分别是-O0,-O1,-O2,-O3以及-Os.之前一直觉得既然是优化选项,顶多是优化一下逻辑,提高一些效率或者减少一下程序大小而已.很少会觉得它 ...
- 控制反转(IoC)与依赖注入(DI)
1.控制反转(Inversion of Control)与依赖注入(Dependency Injection) 控制反转即IoC (Inversion of Control),它把传统上由程序代码直接 ...
- Hadoop 2.6.0编译on mac
花了一个晚上的时间弄了下hadoop的编译环境,碰到些错误,这里保存下. 需要编译Hadoop,不但需要安装Maven,还需要安装protobuf 安装Maven 下载:apache-maven-3. ...
- hadoop 2.x 安装包目录结构分析
bin:Hadoop最基本的管理脚本和使用脚本所在目录,这些脚本是sbin目录下管理脚本的基础实现,用户可以直接使用这些脚本管理和使用hadoop etc:Hadoop配置文件所在目录,包括core- ...