1. 简介

在上一篇介绍<Java GC - 垃圾回收机制>, 本文将介绍如何监控 Javc GC 行为,同时涉及一些GUI工具的使用(虽然有些已经很老并不再更新),监控GC在于判断JVM是否在良好高效地工作并且是否需要投入性能调优(主要包括应用程序优化与JVM参数优化),关注的数据大概有:

1. Mirror GC频率、持续时间以及回收内存量。

2. Major GC频率、持续时间、回收内存量以及 stop-the-world 耗时。

3. Heap 对象分配(导出.hprof文件分析,通常比较大)

2. GC LOG 及Collector行为分析

LOG分析包含前面文章所介绍的各个GC collector的行为分析。通过加入 -XX:+PrintGCDetails 参数则可以打印详细GC信息至控制台。参数-verbose:gc也是可以,但不够详细。通过加入-XX:+PrintGCDateStamps则可以记录GC发生的详细时间。

通过加入 -Xloggc:/home/XX/gc/app_gc.log 可以把GC输出至文件,这对长时间服务器GC监控很有帮助。以下列出一些参数大致打印的信息如下:

1. -verbose:gc: [GC 72104K->9650K(317952K), 0.0130635 secs]

2. -XX:+PrintGCDetails: [GC [PSYoungGen: 142826K->10751K(274944K)] 162800K->54759K(450048K), 0.0609952 secs] [Times: user=0.13 sys=0.02, real=0.06 secs]

3. -XX:+PrintGCDetails 加上-XX:+PrintGCDateStamps 参数则打印如下:

2015-12-06T12:32:02.890+0800: [GC [PSYoungGen: 142833K->10728K(142848K)] 166113K->59145K(317952K), 0.0792023 secs] [Times: user=0.22 sys=0.00, real=0.08 secs] 

可以看出,如果是想监控详细信息与GC发生时间,加上-XX:+PrintGCDateStamps -XX:+PrintGCDetails 参数会是一个比较好的选择。

首先来说明一段在各个GC中通用的字段含义说明:

1、142826K->10751K(274944K) 分别代表回收前、回收后以及总内存大小。

2、Times: user=0.46 sys=0.05, real=0.07 secs:  user代表GC 需要的各个CPU总时间(各个CPU时间相加),sys代表回收器自身的行为所占用CPU时间,real则代表本次GC所耗费的真正耗时(在多核CPU中并行回收,它通常小于user) 。

2.1  Serial GC (-XX:+UseSerialGC)

下面是一段的Serial GC日志含义依次分解:

---------------------------

[GC[DefNew: 78656K->8704K(78656K), 0.0487492 secs] 135584K->80553K(253440K), 0.0488309 secs] [Times: user=0.05 sys=0.00, real=0.05 secs]

[Full GC[Tenured: 62546K->60809K(174784K), 0.1600120 secs] 85931K->60809K(253440K), [Perm : 38404K->38404K(65536K)], 0.1600814 secs] [Times: user=0.16 sys=0.00, real=0.16 secs]

---------------------------

1. 其中的DefNew代表单线程回收yong generation。

2. 紧跟后面的 78656K->8704K(78656K) 中的 78656K 代表young generation 回收前大小,8704K 代表回收后大小,括号中的78656K 代表young generation总大小(包含2个survivor)。

3. 135584K->80553K(253440K) 则代表整个Heap(young+old)的变化与总量,含义参照前面所述(Perm也一样)。

4. Full GC 即代表 major GC, Tenured: 62546K->60809K(174784K)则表示 old generation变化及总量

2.2 Parallel GC

Paralle GC 通过加入参数 -XX:+UseParallelGC 来指定(很多服务器默认不需要加这参数就默认使用该GC collector -- 通过判断配置来决定),通过再加入 -XX:+UseParallelOldGC 使得 Full GC也启用并行,但在(http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html)有如下介绍:

-XX:-UseParallelGC:Use parallel garbage collection for scavenges. (Introduced in 1.4.1)
-XX:-UseParallelOldGC:Use parallel garbage collection for the full collections. Enabling this option automatically sets -XX:+UseParallelGC. (Introduced in 5.0 update 6.)

如想详细查询是否真正自动启用(还是不加入-XX:+UseParallelOldGC有什么细微区别) 则可查看 Open JDK 参考实现来确定,本文不做详细分析,所以加上-XX:+UseParallelGC -XX:+UseParallelOldGC 会是个保险的选择。下面附上一段Open JDK 7的路径为:

hotspot/src/share/vm/runtime/arguments.cpp 部分源码,估计会有些帮助:

void Arguments::set_parallel_gc_flags() {
  assert(UseParallelGC || UseParallelOldGC, "Error");
  // If parallel old was requested, automatically enable parallel scavenge.
  if (UseParallelOldGC && !UseParallelGC && FLAG_IS_DEFAULT(UseParallelGC)) {
    FLAG_SET_DEFAULT(UseParallelGC, true);
  }

  // If no heap maximum was requested explicitly, use some reasonable fraction
  // of the physical memory, up to a maximum of 1GB.
  if (UseParallelGC) {
    FLAG_SET_ERGO(uintx, ParallelGCThreads,
    Abstract_VM_Version::parallel_worker_threads());

    // If InitialSurvivorRatio or MinSurvivorRatio were not specified, but the
    // SurvivorRatio has been set, reset their default values to SurvivorRatio +
    // 2. By doing this we make SurvivorRatio also work for Parallel Scavenger.
    // See CR 6362902 for details.
    if (!FLAG_IS_DEFAULT(SurvivorRatio)) {
      if (FLAG_IS_DEFAULT(InitialSurvivorRatio)) {
        FLAG_SET_DEFAULT(InitialSurvivorRatio, SurvivorRatio + 2);
      }
      if (FLAG_IS_DEFAULT(MinSurvivorRatio)) {
        FLAG_SET_DEFAULT(MinSurvivorRatio, SurvivorRatio + 2);
      }
    }

    if (UseParallelOldGC) {
      // Par compact uses lower default values since they are treated as
      // minimums. These are different defaults because of the different
      // interpretation and are not ergonomically set.
      if (FLAG_IS_DEFAULT(MarkSweepDeadRatio)) {
        FLAG_SET_DEFAULT(MarkSweepDeadRatio, 1);
      }
      if (FLAG_IS_DEFAULT(PermMarkSweepDeadRatio)) {
        FLAG_SET_DEFAULT(PermMarkSweepDeadRatio, 5);
      }
    }
  }
  if (UseNUMA) {
    if (FLAG_IS_DEFAULT(MinHeapDeltaBytes)) {
      FLAG_SET_DEFAULT(MinHeapDeltaBytes, 64*M);
    }
  }
}

--------------------------

[GC [PSYoungGen: 88524K->10728K(274944K)] 133505K->61187K(450048K), 0.0374438 secs] [Times: user=0.08 sys=0.00, real=0.04 secs]
[Full GC [PSYoungGen: 10728K->0K(274944K)] [ParOldGen: 50459K->50210K(175104K)] 61187K->50210K(450048K) [PSPermGen: 38656K->38643K(77312K)], 0.3787131 secs] [Times: user=0.98 sys=0.02, real=0.38 secs]

--------------------------

1. PSYoungGen 代表并行回收 young generation

2. ParOldGen 代表并行回收 old generation.

3. PSPermGen 代表并行回收 Permanent generation. 其他的参数与前面解释的类似。

2.3 CMS GC

CMS GC相对来说比较复杂,通过使用 -XX:+UseConcMarkSweepGC 参数在指定,但是一般情况需要更多的其他参数来保证它能比较好地达到我们的低延时目的,下面就部分常用参数做介绍:

-XX:+CMSIncrementalMode     采用增量式的标记方式,减少标记时应用停顿时间
-XX:+CMSParallelRemarkEnabled     启用并行标记
-XX:CMSInitiatingOccupancyFraction=70     Old generation消耗比例达到多少时进行回收,通常配置60-80之间
-XX:CMSFullGCsBeforeCompaction=1     多少次Full GC 后压缩old generation一次
-XX:+UseCMSInitiatingOccupancyOnly
-XX:+ScavengeBeforeFullGC             Old generation GC前对young generation GC一次,默认开启。
-XX:+CMSScavengeBeforeRemark     CMS remark之前进行一次young generation GC

关于CMSFullGCsBeforeCompaction 的参数影响在Open JDK中如下判断:

*should_compact =
UseCMSCompactAtFullCollection &&
((_full_gcs_since_conc_gc >= CMSFullGCsBeforeCompaction) ||
GCCause::is_user_requested_gc(gch->gc_cause()) ||
gch->incremental_collection_will_fail(true /* consult_young */));

关于UseCMSInitiatingOccupancyOnly  参数的详细解释如下:

-XX:+UseCMSInitiatingOccupancyOnly instructs the HotSpot VM to always use the -XX:CMSInitiatingOccupancyFraction as the occupancy of the old generation space to initiate a CMS cycle.

If -XX:+UseCMSInitiatingOccupancyOnly is not used, the HotSpot VM uses the -XX:CMSInitiatingOccupancyFraction as the occupancy percentage to start only the first CMS cycle. It then attempts to adaptively adjust when to start the CMS cycle for subsequent CMS cycles, that is, it no longer uses the specified -XX:CMSInitiatingOccupancyFraction after the first CMS cycle

下面为一个可参考的CMS GC配置,根据应用的不同用途做相应修改(下面打印了GC LOG):

------------------------------------------------------

JAVA_OPTS="
-DappName=XXX
-server
-Xms10g -Xmx10g
-XX:NewSize=4g -XX:MaxNewSize=4g
-XX:PermSize=256m -XX:MaxPermSize=256m

-XX:+UseConcMarkSweepGC
-XX:+CMSIncrementalMode -XX:+CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction=70 -XX:CMSFullGCsBeforeCompaction=1
-XX:+UseCMSInitiatingOccupancyOnly
-XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark

-XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Xloggc:/home/XX/gc/XX_gc.log
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=10M

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/XX/dump_OOME.hprof

-XX:+DisableExplicitGC

"

-----------------------------------------------------

下面使用一段LOG 作为 CMS GC行为:

--------------------------------

[GC [ParNew: 5033216K->629120K(5662336K), 19.9680140 secs] 5033216K->2091720K(11953792K), 19.9682480 secs] [Times: user=119.82 sys=14.07, real=19.97 secs]

[GC [1 CMS-initial-mark: 1462600K(6291456K)] 4404491K(11953792K), 3.6824630 secs] [Times: user=3.67 sys=0.01, real=3.69 secs]
[CMS-concurrent-mark-start]
[[ParNew: 5662336K->629120K(5662336K), 6.8885140 secs] 7124936K->4366353K(11953792K), 6.8886670 secs] [Times: user=136.94 sys=0.92, real=6.89 secs]
[CMS-concurrent-mark: 2.053/75.039 secs] [Times: user=192.12 sys=3.19, real=75.04 secs]
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 6.159/6.274 secs] [Times: user=7.53 sys=0.30, real=6.28 secs]
[CMS-concurrent-abortable-preclean-start]
[CMS-concurrent-abortable-preclean: 10.680/10.681 secs] [Times: user=12.77 sys=0.43, real=10.68 secs]
[GC[YG occupancy: 3043648 K (5662336 K)] [GC [ParNew: 3043648K->629120K(5662336K), 1.5345480 secs] 6780882K->4985422K(11953792K), 1.5346490 secs] [Times: user=30.72 sys=0.27, real=1.53 secs]
[Rescan (parallel) , 0.1900960 secs] [weak refs processing, 0.0000460 secs] [scrub string table, 0.0008680 secs] [1 CMS-remark: 4356302K(6291456K)] 4985422K(11953792K), 1.7259240 secs] [Times: user=34.88 sys=0.27, real=1.72 secs]
[CMS-concurrent-sweep-start]
[CMS-concurrent-sweep: 3.379/3.380 secs] [Times: user=4.05 sys=0.13, real=3.38 secs]
[CMS-concurrent-reset-start]
[CMS-concurrent-reset: 0.030/0.030 secs] [Times: user=0.02 sys=0.01, real=0.03 secs]

---------------------------------

默认情况下Full GC之前会进行一次 Mirror GC,日志中的第一行则是,含义和前面的描述一样,不再赘述。

其他的则是CMS GC 的各个周期(在前一篇文章中有描述),其中只在 CMS-remark阶段应用暂停时间最长,但相对Parallel GC来说它相对会短些。

3. GC监控工具

GC 监控工具有JDK自带的工具和第三方分析工具,同时包含命令行与GUI工具

附: jstack 命令可以查询当前应用线程状态,可用于判断是否存在死锁、线程等待原因等问题。

3.1 jmap

jmap 有几项参数,直接执行jmap命令可打印这些选项,下面列出 -heap 参数打印情况(关键点粗体字标出):

--------------------------------

Server compiler detected.
JVM version is 24.79-b02

using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC

Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 2147483648 (2048.0MB)
NewSize = 1310720 (1.25MB)
MaxNewSize = 348913664 (332.75MB)
OldSize = 5439488 (5.1875MB)
NewRatio = 2
SurvivorRatio = 8
PermSize = 67108864 (64.0MB)
MaxPermSize = 268435456 (256.0MB)
G1HeapRegionSize = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 80543744 (76.8125MB)
used = 13761040 (13.123550415039062MB)
free = 66782704 (63.68894958496094MB)
17.08517547930228% used
Eden Space:
capacity = 71630848 (68.3125MB)
used = 4848144 (4.6235504150390625MB)
free = 66782704 (63.68894958496094MB)
6.768234825308783% used
From Space:
capacity = 8912896 (8.5MB)
used = 8912896 (8.5MB)
free = 0 (0.0MB)
100.0% used
To Space:
capacity = 8912896 (8.5MB)
used = 0 (0.0MB)
free = 8912896 (8.5MB)
0.0% used
concurrent mark-sweep generation:
capacity = 178978816 (170.6875MB)
used = 54910688 (52.366912841796875MB)
free = 124068128 (118.32058715820312MB)
30.679992876922373% used
Perm Generation:
capacity = 67108864 (64.0MB)
used = 37996544 (36.236328125MB)
free = 29112320 (27.763671875MB)
56.6192626953125% used

17968 interned Strings occupying 2174744 bytes.

--------------------------------

下面dump一份heap分配情况:

jmap -dump:format=b,file=d:/test.hprof 7880

其中 7880为 PID,导出的文件可使用Memory Analyzer, jvisualvm, jprofile等工具打开

3.2 jhat

如果你导出的 hprof 文件很大不方便下载本地分析,可以使用 jhat 启动HTTP Server的后可以使用浏览器访问,

例如:执行 jhat d:/test.hprof 后开启 7000 端口提供HTTP服务,使用浏览器即可访问:

---------------------------

Snapshot read, resolving...
Resolving 2049558 objects...
Chasing references, expect 409 dots
...................................
Eliminating duplicate references...
...................................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

---------------------------

注:如果导出的文件很大,则需要开启更多的内存空间,使用  -J-Xmx5g 命令则开启最大5G。

3.3 jstat (jstatd) & jvisualvm

jstat用于实时监测 GC情况,如PID为7880的应用监测,每 1000毫秒打印一次:

---------------------------

jstat -gc 7880 1000
S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
8704.0 8704.0 8704.0 0.0 69952.0 14761.0 277100.0 181950.8 65536.0 37705.5 60 2.946 35 1.285 4.231
8704.0 8704.0 8704.0 0.0 69952.0 14763.0 277100.0 181950.8 65536.0 37705.5 60 2.946 35 1.285 4.231

---------------------------

如果开启 jstatd 则可远程使用 jvisualvm 的图形化监控.

1. 首先需要配置一个policy文件,内容如下( 假设名称为 tools.policy,根据安装路径不一进行相应修改 ):

grant codebase "file:/usr/java/jdk1.7.0_79/lib/tools.jar" {
permission java.security.AllPermission;
};

2. 然后启动jstatd服务,后面的IP地址为该服务绑定的地址:

jstatd -J-Djava.security.policy=/home/XXX/tools.policy -J-Djava.rmi.server.hostname=192.168.X.X

3. 在jvisualvm中添加 Remote,输入IP新建主机后右键添加 jstatd connection,稍等片刻即可连接,如下图:

注:需要在 TOOLS->PLUGINs 中安装一些插件,如visual GC等才能查看一些更好的信息,如下图:

4. jconsole & jps

jconsole大部分功能在jvisualvm中存在,所以使用的概率相对较小。jps 则是打印目前主机中正在启动的JAVA应用的PID。

5. GCViewer 

该工具用于统计加上参数如-XX:+PrintGCDetails -Xloggc:/home/xx.log 形成的LOG文件,从服务器上拿到文件后启动GCViewer打开,如下图:

6. Memory Analyzer

Memory Analyzer 用户分析 Heap dump 的 .hprof文件,展示目前Heap中的大对象等信息,如下图:

另外还有jProfiler,HPjmeter 等工具不再一一列出。

3. 总结

GC 分析主要点在于:

1. Mirror & Major GC 情况

2. Heap 对象分配

Java GC - 监控回收行为与日志分析的更多相关文章

  1. 面试官,不要再问我“Java GC垃圾回收机制”了

    Java GC垃圾回收几乎是面试必问的JVM问题之一,本篇文章带领大家了解Java GC的底层原理,图文并茂,突破学习及面试瓶颈. 楔子-JVM内存结构补充 在上篇<JVM之内存结构详解> ...

  2. Java GC 垃圾回收算法 内存分配

    垃圾回收(Garbage Collection, GC)是Java不同于c与c++的重要特性之一. 他帮助Java自动清空堆中不再使用的对象. 由于不需要手动释放内存,程序员在编程中也可以减少犯错的机 ...

  3. Java GC - 垃圾回收机制

    1.简介 对于Java developer来说,了解JVM GC工作原理能够帮助我们开发出更优秀的应用,同时在处理JVM瓶颈时能够更加自由.在最近一年的应用开发中能体会到这些知识带来的好处,并且让我们 ...

  4. Android内存优化3 了解java GC 垃圾回收机制1

    开篇废话 如果我们想要进行内存优化的工作,还是需要了解一下,但这一块的知识属于纯理论的,有可能看起来会有点枯燥,我尽量把这一篇的内容按照一定的逻辑来走一遍.首先,我们为什么要学习垃圾回收的机制,我大概 ...

  5. Android内存优化4 了解java GC 垃圾回收机制2 GC执行finalize的过程

    1. finalize的作用 finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法. finalize()与C++中的析构函数 ...

  6. 乐字节Java|GC垃圾回收机制、package和import

    本文接上一篇:乐字节Java|this关键字.static关键字.block块.本文是接着讲述JavaGC垃圾回收机制.package 和 import语句. 一.GC垃圾回收机制 GC全名:Garb ...

  7. 白话说java gc垃圾回收

    gc是java区别于其他好几门语言(c/c++)的一个代表功能(当然也有很多可以自动管理内存的语言,如所有的脚本语言,你根本不知道内存管理这回事)! 当然,之所以要把c/c++和java相比,是因为j ...

  8. Android内存优化5 了解java GC 垃圾回收机制3

    引言 接App优化之内存优化(序), 作为App优化系列中内存优化的一个小部分. 由于内存相关知识比较生涩, 内存优化中使用到的相关工具, 也有很多专有名词. 对Java内存管理, GC, Andro ...

  9. JAVA gc垃圾回收机制

    一.GC概要   JVM堆相关知识     为什么先说JVM堆?     JVM的堆是Java对象的活动空间,程序中的类的对象从中分配空间,其存储着正在运行着的应用程序用到的所有对象.这些对象的建立方 ...

随机推荐

  1. DataTable数据导出Excel 并且下载

    public string Excel(System.Data.DataTable dt) { //模板的路径 string strUploadPath = HttpContext.Current.S ...

  2. ClassLoader机制:一个类何时会被虚拟机初始化?

    版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 大家都知道Java程序被编译器编译成字节码文件保存在硬盘里,Java虚拟机在执行代码时首先要把编译后的字节码文件从硬盘加载到内存中,然后才 ...

  3. java 局部变量几点笔记

    1.局部变量的作用时间很短暂,都被存储在方法的栈内存中:2.(没使用static)非静态变量=实例变量:(使用static)静态变量=类变量3.成员变量:类体内定义的变量:4.局部变量有三种:1)形参 ...

  4. Python 基础 (单、双引号区别) 不断补充

    最近开始学习Python ,一些小细节的东西不是很理解,所以就记录一下,方便自己以后查看. 我的Python环境: Mac pro 10.12.3,Python3.5 ,Pycharm 多句题外话:公 ...

  5. JMS 之 Active MQ 启动嵌入式Broke

    一.如何启动active MQ 服务 (一).使用命令启动 /bin 目录下 ./activemq start 默认使用conf/activemq.xml 配置文件 b.[root@localhost ...

  6. 利用Jsoup包爬取网站内容

    一 Jsoup包 下载链接:http://download.csdn.net/detail/u014000832/7994245 二 爬取搜狐新闻网站标题等内容 package com.test1; ...

  7. Oracle Job定时任务的使用详解

    oracle中的job能为你做的就是在你规定的时间格式里执行存储过程,定时执行一个任务 .下面是一个小案例,定时每15分钟向一张表插入一条数据 一 1.创建一张测试表 -- Create table ...

  8. Jmeter-BeanShell PostProcessor提取请求及响应结果并保存到本地文件

    1.新建一个本地csv文件,存放请求需要使用的变量值account,password,并配置CSV Data Set Config 2.添加一个HTTP请求 3.添加正则提取器用来提取响应结果中的re ...

  9. Akka(10): 分布式运算:集群-Cluster

    Akka-Cluster可以在一部物理机或一组网络连接的服务器上搭建部署.用Akka开发同一版本的分布式程序可以在任何硬件环境中运行,这样我们就可以确定以Akka分布式程序作为标准的编程方式了. 在上 ...

  10. HTML行内元素、块状元素、行内块状元素的区别

    HTML可以将元素分类方式分为行内元素.块状元素和行内块状元素三种.首先需要说明的是,这三者是可以互相转换的,使用display属性能够将三者任意转换: (1)display:inline;转换为行内 ...