






























阅读分析虚拟机和垃圾收集器的日志是处理Java虚拟机内存问题必备的基础技能,为进行GC调优提供数据分析支持。
一、JDK统一日志格式
垃圾收集器日志是一系列人为设定的规则,多少有点随开发者编码时的心情而定,没有任何的“业界标准”可言,换句话说,每个收集器的日志格式都可能不一样。除此以外还有一个麻烦,在JDK 9以前,HotSpot并没有提供统一的日志处理框架,虚拟机各个功能模块的日志开关分布在不同的参数上,日志级别、循环日志大小、输出格式、重定向等设置在不同功能上都要单独解决。直到JDK 9,这种混乱不堪的局面才终于消失,HotSpot所有功能的日志都收归到了-Xlog参数上,这个参数的能力也相应被极大拓展了。
Xlog选项支持的参数如下:
-Xlog[:[what][:[output][:[decorators][:output-options[,...]]]]]
1、what:选择器,由标签(Tag)和日志级别(Level)共同组成。格式:tag1[+tag2...][*][=level][,...] 。除非用*指定了通配符,否则只有匹配了指定tag的日志消息才会被匹配。
(1)标签Tag:可理解为虚拟机中某个功能模块的名字,它告诉日志框架用户希望得到虚拟机哪些功能的日志输出。垃圾收集器的标签名称为“gc”,全部支持的功能模块标签名如下:(如指定为all,则表示下面所有标签的组合)
add、age、alloc、annotation、aot、arguments、attach、barrier、biasedlocking、blocks、bot、breakpoint、bytecode、census、class、classhisto、cleanup、compaction、comparator、constraints、constantpool、coops、cpu、cset、data、defaultmethods、dump、ergo、event、exceptions、exit、fingerprint、freelist、gc、hashtables、heap、humongous、ihop、iklass、init、itables、jfr、jni、jvmti、liveness、load、loader、logging、mark、marking、metadata、metaspace、method、mmu、modules、monitorinflation、monitormismatch、nmethod、normalize、objecttagging、obsolete、oopmap、os、pagesize、parser、patch、path、phases、plab、preorder、promotion、protectiondomain、purge、redefine、ref、refine、region、remset、resolve、safepoint、scavenge、scrub、setting、stackmap、stacktrace、stackwalk、start、startuptime、state、stats、stringdedup、stringtable、subclass、survivor、sweep、system、task、thread、time、timer、tlab、unload、update、verification、verify、vmoperation、vtables、workgang、
(2)日志级别:从低到高有Trace, Debug, Info, Warning, Error, Off六种级别, 日志级别决定了输出信息的详细程度,默认级别为Info,HotSpot的日志规则与Log4j、SLF4j这类Java日志框架大体上是一致的。
2、output:设置输出类型。默认为stdout。
-Xlog 支持以下类型的输出:
3、decorators:装饰器,用来装饰消息,记录与消息有关的信息。使用一系列自定义的装饰器去配置output,来要求每行日志输出都附加上额外的内容。可以为每个output配置一组自定义的装饰器,输出顺序和定义的顺序相同。缺省的装饰器为uptime、level和tags。
支持附加在日志行上的信息包括:
如果不指定, 默认值是uptime、 level、 tags这三个, 此时日志输出类似于以下形式:
[3.080s][info][gc,cpu] GC(5) User=0.03s Sys=0.00s Real=0.01s
4、output-options:设置Xlog的日志输出output选项,格式:
filecount=file-count filesize=file size with optional K, M or G suffix
5、使用示例
# 示例1:使用info级别记录所有信息到stdout,装饰器使用uptime、level及tags # 等价于-Xlog:all=info:stdout:uptime,levels,tags -Xlog # 示例2:以info级别打印使用了gc标签的日志到stdout -Xlog:gc # 示例3:使用默认装饰器,info级别,将使用gc或safepoint标签的消息记录到stdout。 # 如果某个日志同时标签了gc及safepoint,不会被记录,逗号表示或 -Xlog:gc,safepoint # 示例4:使用默认装饰器,debug级别,打印同时带有gc和ref标签的日志。 # 仅使用gc或ref的日志不会被记录,加号表示同时 -Xlog:gc+ref=debug # 示例5:不使用装饰器,使用debug级别,将带有gc标签的日志记录到gc.txt中 -Xlog:gc=debug:file=gc.txt:none # 示例6:以trace级别记录所有带有gc标签的日志到gctrace.txt文件集中,该文件集中的文件最大1M,保留5个文件;使用的装饰器是uptimemillis、pid -Xlog:gc=trace:file=gctrace.txt:uptimemillis,pids:filecount=5,filesize=1024 # 示例7:使用trace级别,记录至少带有gc及meta标签的日志到gcmetatrace.txt,同时关闭带有class的日志。某个消息如果同时带有gc、meta及class,将不会被记录,因为class标签被关闭了。 -Xlog:gc+meta*=trace,class*=off:file=gcmetatrace.txt
6、为运行中的 JVM 上配置日志记录
jdk9及其以上版本:可以使用 jcmd 和 VM.log在JVM上配置VM日志记录。所有java命令配置VM日志记录时提供的所有配置选项都可使用,但是需要通过 output、output_options、what 和 decorators 参数指定,下面是一个 jcmd 的使用示例:
jcmd <pid> VM.log output="file=heap.log" output_options="filecount=5,filesize=10m" what="heap=info" decorators="time,level"
二、旧式GC日志和Xlog的对照
|
旧式GC日志配置 |
Xlog配置 |
作用 |
|
PrintGCID |
无配置,Xlog总是会记录GC ID |
GC 日志里会有#的 ID |
|
PrintGCCause |
无配置,Xlog总是会记录GC cause |
GC 原因 |
|
PrintGC |
-Xlog:gc |
查看GC基本信息 |
|
PrintGCDetails |
-Xlog:gc* |
查看GC详细信息 |
|
PrintHeapAtGC |
-Xlog:gc+heap=debug |
查看GC前后的堆、方法区可用容量变化 |
|
PrintHeapAtGCExtended |
-Xlog:gc+heap=trace |
如果设置了PrintHeapAtGC会打印额外的信息 |
|
PrintGCApplicationConcurrentTime |
-Xlog:safepoint |
查看GC过程中用户线程并发时间 |
|
PrintGCApplicationStoppedTime |
查看GC过程中用户线程停顿时间 |
|
|
PrintAdaptiveSizePolicy |
-Xlog:gc+ergo*=trace |
查看收集器Ergonomics机制( 自动设置堆空间各分代区域大小、 收集目标等内容, 从Parallel收集器开始支持)自动调节的相关信息 |
|
PrintTenuringDistribution |
-Xlog:gc+age*=trace |
查看熬过收集后剩余对象的年龄分布信息 |
|
PrintTerminationStats |
-Xlog:gc+task+stats=debug |
打印并行收集器的终止统计信息 |
|
PrintClassHistogramAfterFullGC |
-Xlog:classhisto*=trace |
在FullGC结束后打印类直方图 |
|
PrintClassHistogramBeforeFullGC |
在FullGC开始前打印类直方图 |
|
|
PrintGCTimeStamps |
使用time修饰器 |
输出GC的时间戳(以基准时间的形式) |
|
PrintGCDateStamps |
使用uptime修饰器 |
输出GC的时间戳(以日期的形式,如2013-05-04T21:53:59.234+0800 ) |
|
PrintGCTaskTimeStamps |
-Xlog:gc+task=debug |
打印各个gc工作线程任务的时间戳 |
|
PrintJNIGCStalls |
-Xlog:gc+jni=debug |
当GC被JNl关键部分暂停时打印诊断消息 |
|
PrintPLAB |
-Xlog:gc+plab=trace |
打印(survivor区)用于担保推断的空间大小 |
|
PrintOldPLAB |
打印(老年代区)用于担保推断的空间大小 |
|
|
PrintParallelOldGCPhaseTimes |
-Xlog:gc+phases=trace |
打印并行执行老年代GC的次数 |
|
PrintPromotionFailure |
-Xlog:gc+promotion=debug |
打印新生代升级老年代失败时的诊断信息 |
|
PrintReferenceGC |
-Xlog:gc+ref=debug |
打印GC期间处理引用对象所花费的时间,PrintReferenceGC需要结合PrintGCDetails才能使用 |
|
PrintStringDeduplicationStatistics |
-Xlog:gc+stringdedup |
打印详细的重复数据删除统计信息 |
|
PrintTaskqueue |
-Xlog:gc+task+stats=trace |
打印并行收集器的任务队列统计信息 |
|
PrintTLAB |
-Xlog:gc+tlab=trace |
打印各种TLAB相关信息(增加了-XX:+TLABStats) |
|
TraceAdaptiveGCBoundary |
-Xlog:heap+ergo=debug |
追踪新生代-老年代边界移动 |
|
TraceDynamicGCThreads |
-Xlog:gc+task=trace |
跟踪动态GC线程的使用情况 |
|
TraceMetadataHumongousAllocation |
-Xlog:gc+metaspace+alloc=debug |
跟踪庞大的元数据分配 |
|
TraceExceptions |
-Xlog:exceptions=info |
跟踪异常 |
|
TraceClassLoading |
-Xlog:class+load=level |
跟踪类加载信息 |
|
-verbose:class |
-Xlog:class+load=info,class+unload=info |
跟踪类加载、卸载信息 |
|
TraceClassLoadingPreorder |
-Xlog:class+preorder=debug |
跟踪被引用到的所有类的加载信息 |
|
TraceClassUnloading |
-Xlog:class+unload=debug |
跟踪类卸载信息 |
|
VerboseVerification |
-Xlog:verification=info |
输出与验证相关的详细诊断信息 |
|
TraceClassPaths |
-Xlog:class+path=info |
打印出具体bootstrap路径、classpath、path |
|
TraceClassResolution |
-Xlog:class+resolve=debug |
输出哪个类来自哪个文件 |
|
TraceClassInitialization |
-Xlog:class+init=info |
跟踪类初始化 |
|
TraceLoaderConstraints |
-Xlog:class+loader+constraints=info |
跟踪类加载器约束的相关信息 |
|
TraceClassLoaderData |
-Xlog:class+loader+data=info |
跟踪类加载器相关信息 |
|
TraceSafepointCleanupTime |
-Xlog:safepoint+cleanup=info |
跟踪安全点期间执行的清理任务的耗时 |
|
TraceSafepoint |
-Xlog:safepoint=debug |
跟踪安全点 |
|
TraceMonitorInflation |
-Xlog:monitorinflation=debug |
跟踪监视器锁扩张 |
|
TraceBiasedLocking |
-Xlog:biasedlocking=debug |
跟踪偏置锁 |
|
TraceRedefineClasses |
-Xlog:redefine+class*=debug |
跟踪重置类 |
|
GCLogFileSize |
-Xlog:gc*:file=<file>::filecount=<count>,filesize=<filesize in Kb> |
GC日志文件大小 |
|
NumberOfGCLogFiles |
GC日志文件数量 |
|
|
UseGCLogFileRotation |
是否使用GC日志轮转 |
|
|
G1PrintHeapRegions |
-Xlog:gc+region=trace |
如果设置为G1,则将打印关于正在分配哪些区域和回收哪些区域的信息 |
|
G1PrintRegionLivenessInfo |
-Xlog:gc+liveness=trace |
打印在清除阶段每个Region存活对象信息 |
|
G1SummarizeConcMark |
-Xlog:gc+marking=trace |
汇总并发标记信息 |
|
G1SummarizeRSetStats |
-Xlog:gc+remset*=trace |
可以确定并发优化线程是否能够及时处理更新日志缓冲区,并提供更多的信息,来帮助为RSet粗化总数提供窗口 |
|
G1TraceConcRefinement |
-Xlog:gc+refine=debug |
跟踪G1并发细化 |
|
G1TraceEagerReclaimHumongousObjects |
-Xlog:gc+humongous=debug |
在每个新生代GC上打印一些关于大对象存活对象的信息 |
|
G1TraceStringSymbolTableScrubbing |
-Xlog:gc+stringtable=trace |
跟踪信息字符串和符号表清理 |
通用的获取GC日志JVM参数(其他参数根据需要添加):
#jdk6-8: -XX:+PrintGCDetails -XX:+PrintGCCause -XX:+PrintGCDateStamps -Xloggc:./gc.log #jdk9及以上版本: -Xlog:gc*:file=./gc.log
三、GC日志分析工具
1、基础命令工具-jstat:虚拟机统计信息监视工具(JVM Statistics Monitoring Tool)
用于监视虚拟机各种运行状态信息的命令行工具。 它可以显示本地或者远程虚拟机进程中的类加载、内存、垃圾收集、即时编译等运行时数据, 在没有GUI图形界面、只提供了纯文本控制台环境的服务器上, 它将是运行期定位虚拟机性能问题的常用工具。
|
命令格式:jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]] 通过jstat -help查看当前jdk版本支持的格式 源码:tools.jar:sun.tools.jstat.Jstat |
|||
|
-help或-h |
列出命令帮助信息 |
||
|
-option |
用户希望查询的虚拟机信息, 主要分为三类: 类加载、 垃圾收集、 运行期编译状况。可以使用命令:jstat -options列出所有查询类型 |
||
|
类加载 |
-class |
监视类加载、卸载数量、总空间以及类装载所耗费的时间 |
|
|
垃圾收集 |
-gc |
监视Java堆状况,包括Eden区、2个Surviver区、老年代、永久代等的容量,已用空间,垃圾收集时间合计等信息 |
|
|
-gccapacity |
监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间 |
||
|
-gcutil |
监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比 |
||
|
-gccause |
监视内容与-gc基本相同,但是会额外输出导致上一次垃圾收集产生的原因 |
||
|
-gcnew |
监视新生代垃圾收集状况 |
||
|
-gcnewcapacity |
监视内容与-gcnew基本相同,但输出主要关注使用到的最大、最小空间 |
||
|
-gcold |
监视老年代垃圾收集状况 |
||
|
-gcoldcapacity |
监视内容与-gcold基本相同,但输出主要关注使用到的最大、最小空间 |
||
|
-gcpermcapacity |
输出永久代使用到的最大、最小空间 |
||
|
运行期编译状况 |
-compiler |
输出即时编译器编译过的方法、耗时等信息 |
|
|
-printcompilation |
输出已经被即时编译的方法 |
||
|
-t |
把时间戳列显示为输出的第一列。这个时间戳是从Java虚拟机的开始运行到现在的秒数。 |
||
|
-h<lines> |
每显示lines行显示一次表头,其中lines为正整数。默认值为 0,即仅在第一行数据显示一次表头。例:-h5 每5行打印一次行头 |
||
|
vmid |
java进程ID |
||
|
interval |
显示信息的时间间隔,单位默认毫秒。也可以指定秒为单位,比如:1s。如果指定了该参数,jstat命令将每个这段时间显示一次统计信息。例:jstat -gc 32586 1000 等价于 jstat -gc 32586 1s |
||
|
count |
显示数据的次数,默认值是无穷大,这将导致jstat命令一直显示统计信息,直到目标JVM终止或jstat命令终止 |
||
2、可视化工具-VisualGC
VisualGC可以作为VisualVM插件使用。一般用于开发、测试阶段,生产环境一般不会放开JMX端口。

|
编译时间面板:绘制了将Java字节码编译为本机代码所花费的时间。此显示的高度不会缩放到任何特定值。此图中的非零值表示上一个间隔期间进行了编译活动。窄脉冲表示相对短的持续时间,宽脉冲表示长的持续时间。标题栏指示自应用程序启动以来的编译任务数和累计编译时间。 |
|
类载入时间面板:绘制了在类加载和卸载活动中花费的时间。此显示的高度不会缩放到特定值。此图中的非零值表示上一个间隔期间进行了类加载活动。窄脉冲表示相对短的持续时间,宽脉冲表示长的持续时间。标题栏指示自应用程序启动以来加载和卸载的类的数量以及累积的类加载时间。 |
|
GC耗时面板:绘制垃圾收集活动的时间量。此显示的高度不会缩放到任何特定值。此图中的非零值表示上一个间隔期间进行了垃圾收集活动。窄脉冲表示相对短的持续时间,宽脉冲表示长的持续时间。标题栏指示自应用程序启动以来GC总次数和累计GC时间,以及上次触发GC原因:allocation Failure 内存分配失败(eden满了或老年代,元空间满了) |
|
Eden区面板:绘制了Eden空间随时间的利用情况。Eden是构成年轻代的三个空间之一。此面板的高度是固定的,默认情况下,数据会根据空间的当前容量进行缩放。由于空间的当前容量可以根据收集器策略动态变化,因此随着时间的推移,空间可能会收缩和增长,从而出现视觉伪影。括号中(532.562M,532.562M)显示Eden最大容量和当前容量,433.463M表示已使用多少内存。69 collections表示发生了Young GC,累积耗时2.590s。 |
|
Survivor 0 and Survivor 1面板:绘制了两个Survivor空间随时间的利用情况。Survivor空间是构成年轻代的剩余两个空间。每个面板的高度都是固定的,默认情况下,数据会根据相应空间的当前容量进行缩放。由于Survivor空间的当前容量可能会根据收集器策略而动态变化,因此随着空间随着时间的推移而缩小和增长,可能会出现视觉伪影。 标题栏在括号中显示空间的名称及其最大容量和当前容量,后跟空间的当前内存使用量。 |
|
Old区面板:绘制了老年代在一段时间内的利用率。老年代由一个单独的空间组成。面板的高度是固定的,默认情况下,数据会根据空间的当前容量进行缩放。由于空间的当前容量可以根据收集器策略动态变化,因此随着时间的推移,空间可能会收缩和增长,从而出现视觉伪影。标题栏在括号中显示空间的名称及其最大容量和当前容量,后跟空间的当前利用率。此外,标题还包含发生FullGC数量和累积时间。 |
|
元数据区面板:绘制了一段时间内永久代利用率。永久代由一个空间组成,但没有自己的收集器。相反,这个空间是作为老年代的一部分收集的。面板的高度是固定的,默认情况下,数据会根据空间的当前容量进行缩放。由于空间的当前容量可以根据收集器策略动态变化,因此随着时间的推移,空间可能会收缩和增长,从而出现视觉伪影。标题栏在括号中显示空间的名称及其最大容量和当前容量,后跟空间的当前利用率。 |
|
每个GC空间图都可以以两种模式之一显示——保留模式或提交模式,其中提交模式是默认模式。在保留模式下,根据空间的最大容量缩放数据,而在提交模式下,则根据空间的当前容量缩放数据。在保留模式下,背景网格涂成深灰色以表示保留内存中未提交内存的部分,并涂成绿色以表示已提交的部分。可以通过右键单击空间并选中或取消选中“显示保留空间”复选框来切换模式。 |
|
幸存者年龄直方图:幸存者年龄直方图窗口是最下面的窗口。它由两个面板组成,参数面板和直方图面板。 参数面板显示幸存者空间的大小以及控制新生代晋升行为的参数。在每个Young GC之后,在该GC中幸存但未升级的对象将保留在Survivor空间中。每当对象在Young GC中存活下来时,对象的年龄都会增加,直到对象的年龄达到TenuringThreshold变量定义的最大年龄,该变量在1和MaxTenuringThreshold-1之间变化,具体取决于幸存者空间的利用率。如果幸存者空间溢出,则将最旧的对象提升到老年代,直到空间的利用率不超过DesiredSurvivorSize。MaxTenuringThreshold值为0将导致对象始终升级到老年代,而值为32将阻止对象升级,除非Survivor空间已满。 Tenuring Threshold:表示新生代年龄大于当前值则进入老年代; Max Tenuring Threshold:表示新生代最大年龄值; Tenuring Threshold与Max Tenuring Threshold区别:Max Tenuring Threshold是一个最大限定,所有的新生代年龄都不能超过当前值,而Tenuring Threshold是个动态计算出来的临时值,一般情况与Max Tenuring Threshold相等,如果在Suivivor空间中,相同年龄所有对象大小的总和大于Survivor空间的一半,则年龄大于或者等于该年龄的对象就都可以直接进入老年代(如果计算出来年龄段是6,则Tenuring Threshold=6,age>=6的Suivivor对象都符合要求),它才是新生代是否进入老年代判断的依据。 Desired Survivor Size:Survivor空间大小验证阙值(默认是survivor空间的一半),用于Tenuring Threshold判断对象是否提前进入老年代。 Current Survivor Size:当前survivor空间大小 直方图面板显示上一次Young GC后Survivor空间中对象年龄分布的快照。显示器由32个大小相同的区域组成,每个区域对应一个可能的物体年龄。每个区域代表100%的活动Survivor空间,并填充有一个彩色区域,该区域指示给定年龄的对象占据的幸存者空间的百分比。 当目标JVM使用并行新生代收集器(-XX:+UseParallelGC)启动时,不会显示Survivor Age Hisprogram窗口。并行新生代收集器不维护幸存者年龄直方图,因为它应用了不同的策略来维护幸存者空间中的对象。 |
注意:
(1) Visual GC插件不支持通过JMX方式连接远端jvm,要通过另一个方式jstatd方式进行连接,否则显示"不受此JVM支持"。
(2)如果VM参数设置-XX:+UseParallelGC 或者 -XX:+AggressiveHeap,直方图Histogram会显示"不受此JVM支持"。
3、GCViewer
GCViewer是一个离线分析Java GC日志可视化小工具。生产项目上线初期调优、业务高峰期、故障等可以打印GC日志下载后离线分析,比看纯文本要直观一些。
GCViewer是一个jar包,可以直接启动:java -jar gcviewer-1.36.jar ,然后导入GC日志文件。

左侧是图表面板,右侧是数据面板。
3.1 Chart选项卡

显示了一系列线条
3.2 Event details选项卡

用于展示GC事件详情
2017-03-29T14:37:12.812+0200: 8.832: [GC (Allocation Failure)[PSYoungGen: 29146K->3457K(29184K)] 78228K->52539K(116736K), 0.0009340 secs][Times: user=0.00 sys=0.00, real=0.00 secs] 2017-03-29T14:37:12.813+0200: 8.833: Total time for which application threads were stopped: 0.0010682 seconds, Stopping threads took: 0.0000155 seconds
3.3 Parser选项卡
显示了解析器的输出。如果在解析过程或其他输出过程中出现警告,可以在那里进行检查。

3.4 Data Panel面板
GCViewer提供了一些指标来帮助解释图表。请注意,一些基于平均值的指标与它们的标准差一起显示。如果与平均值相比,标准偏差明显较大,则这些值将变灰,表明实际值远小于或大于平均值。
(1)Summary 摘要

(2)Memory 内存

(3)Pause

------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
备注:GCViewer本身功能有一些局限性,有些展示可能存在错误,不能完全依赖这个工具展示信息。
其他:
学习技术不是用来写HelloWorld和Demo的,而是要用来解决线上系统的真实问题的.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。