前言
HotSpot VM 作为使用最广泛的 JVM 的,如无歧义,以下谈到 JVM 均指代 HotSpot VM。
TLAB
堆区是线程共享区域,线程在创建新对象时需要在堆中开辟内存。然而在并发的情况下从堆中申请内存并不安全,为了避免多个线程同时操作同一个地址,需要互斥锁等同步机制来保证安全,但这会影响内存分配效率,因而引入了TLAB这个概念。
- 从内存模型而不是垃圾收集的角度,对Eden继续进行划分,JVM为每个线程分配了一个私有缓冲区,它包含在Eden空间内
- 多线程同时分配内存时,使用TLAB可以避免一系列的线程不安全问题,同时因为不需要同步机制从而有效的提高了内存分配吞吐量
一旦对象在TLAB中分配失败(默认情况下TLAB仅占Eden空间的1%,容量非常小),JVM会尝试通过锁机制来确保内存分配的原子性,从而直接在Eden中分配内存。可以通过JVM参数-XX:UseTLAB
开启TLAB支持(默认开启),注意引入了TLAB之后,可以说堆就不是100%线程共享的了。
启动参数
分类
> $ java -jar -server -Dfile.encoding=UTF-8 -Xmx8g -XX:+UseG1GC -XX:MetaspaceSize=512m demo.jar
- 以
-
开头的是标准参数,所有的 JVM 实现都要支持这些参数,并且向后兼容 - 以
-D
开头的是系统参数,可以在代码中通过System.getProperty(String key)
来获取 - 以
-X
开头的是非标准参数,基本都是传给 JVM 的。注意不是所有的 JVM 实现都支持,并且不保证向后兼容。可以使用java -X
命令来查看当前 JVM 支持的非标准参数 - 以
–XX
开头的是非稳定参数,用于控制 JVM 的行为,跟具体的 JVM 实现有关,并且随时可能会在下个版本取消-
-XX:+-flag
形式,+-
表示开启或关闭某个功能 -
-XX:key=value
形式, 指定某个选项的值
-
常见参数
运行模式
-
-Xint
: 在解释模式下运行,-Xint 标记会强制 JVM 解释执行所有的字节码。这当然会降低运行速度,通常低10倍或更多 -
-Xcomp
: -Xcomp 与 -Xint 正好相反,JVM 在第一次使用时会把所有的字节码编译成本地代码,从而带来最大程度的优化 -
-Xmixed
:-Xmixed 是混合模式,将解释模式和编译模式进行混合使用(由 JVM 自己决定),这是 JVM 的默认模式,也是推荐模式。 可以使用java -version
查看 mixed mode 等信息
内存设置
-
-Xmx
,指定最大堆内存,默认物理内存/4
。注意此参数只是限制了 新生代+老年代 的最大值,不包括栈内存,也不包括堆外内存 -
-Xms
,指定起始堆内存,默认物理内存/64
。 注意此参数指定的内存大小,并不是操作系统实际分配的初始值,而是GC先规划好,用到才分配。 建议服务器上保持 –Xms 和 –Xmx 一致,避免因堆内存扩容导致的性能抖动以及FullGC -
-XX:NewSize=
,指定新生代起始容量 -
-XX:MaxNewSize=
,指定新生代最大容量 -
-Xmn
,指定新生代大小,等价于同时设置-XX:NewSize=
和-XX:MaxNewSize=
,使用 G1 垃圾收集器不应该设置该选项,官方建议设置为 -Xmx 的 1/4 ~ 1/2 -
-XX:NewRatio=
,指定老年代与新生代的比例,默认2,与 -Xmn 互斥 -
-XX:SuriviorRatio=
,指定年轻代中Eden区与单个Survivor区的比例,默认8 -
-XX:PermSize=
,指定永久代起始容量, JDK1.8 前使用 -
-XX:MaxPermSize=
,指定永久代最大容量, JDK1.8 前使用 -
-XX:MetaspaceSize=
,指定元空间起始容量,默认 -1 不限制,JDK1.8 后使用 -
-XX:MaxMetaspaceSize=
,指定元空间最大容量,Java 8 默认不限制 Meta 空间,最好不要设置该选项 -
-XX:MaxDirectMemorySize=
,指定最大堆外内存,这个参数和 -Dsun.nio.MaxDirectMemorySize 效果相同 -
-Xss
,指定线程栈大小,影响栈的深度,如 -Xss1m 指定线程栈为 1MB,与 -XX:ThreadStackSize=1m 等价
GC相关
-
-XX:+UseG1GC
,使用 G1 垃圾回收器 -
-XX:+UseConcMarkSweepGC
,使用 CMS 垃圾回收器,ParNew + CMS,关注响应时间 -
-XX:+UseSerialGC
,使用串行垃圾回收器 ,Serial + SerialOld -
-XX:+UseParallelGC
,使用并行垃圾回收器,Parallel + ParallelOld,关注吞吐量(java 7~8默认) -
-XX:+PrintGC
,输出GC日志 -
-XX:+PrintGCDetails
,输出GC的详细日志 -
-XX:+PrintGCTimeStamps
,输出GC的时间戳(以基准时间的形式) -
-XX:+PrintGCDateStamps
,输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800) -
-XX:+PrintHeapAtGC
,在进行GC的前后打印出堆的信息 -
-Xloggc:
,日志文件的输出路径 -
-XX:MaxTenuringThreshold=
,晋升老年代的年龄阈值,使用 SerialGC、ParNew 时有效 -
-XX:PretenureSizeThreshold
,晋升老年代的大小阈值,默认0,即不指定最大的晋升大小,一切由运行情况决定,使用 SerialGC、ParNew 时有效 -
-XX:MaxGCPauseMillis=
,预期 GC 暂停时间,使用 G1 时可用 -
-XX:G1NewSizePercent
,初始年轻代占整个 Java Heap 的大小,默认值为 5% -
-XX:G1MaxNewSizePercent
,最大年轻代占整个 Java Heap 的大小,默认值为 60% -
-XX:G1HeapRegionSize
,设置每个 Region 的大小,单位 MB,需要为 1、2、4、8、16、32 中的某个值,默认是堆内存的 1/2000。如果这个值设置比较大,那么大对象就可以进入 Region 了 -
-XX:+InitiatingHeapOccupancyPercent
,G1 内部并行回收循环启动的阈值,默认为 Java Heap的 45%。这个可以理解为老年代使用大于等于 45% 的时候,JVM 会启动垃圾回收。这个值非常重要,它决定了在什么时间启动老年代的并行回收 -
-XX:G1HeapWastePercent
,G1停止回收的最小内存大小,默认是堆大小的 5%。GC 会收集所有的 Region 中的对象,但是如果下降到了 5%,就会停下来不再收集了。就是说,不必每次回收就把所有的垃圾都处理完,可以遗留少量的下次处理,这样也降低了 单次消耗的时间
-XX:ConcGCThreads=N、-XX:ParallelGCThreads=N
ParallelGCThreads指的是在STW阶段,并行执行垃圾收集动作的线程数。默认情况下,当CPU数量小于8时,ParallelGCThreads的值就是CPU的数量,当CPU数量大于8时,ParallelGCThreads的值等于3+5*cpuCount/8。
ConcGCThreads指的是并发标记阶段用于执行标记的线程数。G1默认启动的并发线程数是ParallelGCThreads/4,而CMS是(ParallelGCThreads+3)/4。
并发是指垃圾收集器和应用程序交替执行,并行是指应用程序停止,同时由多个线程一起执行GC。因此并行回收器不是并发的。因为并行回收器执行时,应用程序完全挂起,不存在交替执行的步骤。
分析诊断
-
-XX:+-HeapDumpOnOutOfMemoryError
,当 OutOfMemoryError 产生时自动 dump 堆内存 -
-XX:HeapDumpPath=
,与 HeapDumpOnOutOfMemoryError 搭配使用,指定内存溢出时 dump 文件的目录。如果没有指定,默认为启动 Java 程序的工作目录 -
-XX:OnError=
,发生致命错误时(fatal error)执行的脚本。 例如写一个脚本记录出错时间,执行一些命令,或者 curl 一下某个在线报警的 url。 示例用法:java -XX:OnError="gdb - %p" DemoApp 可以发现有一个 %p 的格式化字符串,表示进程 PID -
-XX:OnOutOfMemoryError=
,抛出 OutOfMemoryError 时执行的脚本 -
-XX:ErrorFile=
,致命错误的日志文件名,绝对路径或者相对路径
命令行工具
分类
工具 | 简介 |
---|---|
javap | 反编译Class文件 |
jps/jinfo | 查看JVM进程 |
jstat | 查看JVM内部GC相关信息 |
jmap | 查看堆或类占用空间的统计信息 |
jstack | 查看线程信息 |
如何使用
javap
# 格式
javap [options]
# 参数
-v 输出附加信息
-l 输出行号和本地变量表
# 示例
> $ javap -v -l TestClass
jps
# 格式
jps [options]
# 参数
-l 输出启动类详细信息
# 示例
> $ jps
95463 HttpServer
95574 Jps
95462 Launcher
56766
> $ jps -l
95463 com.anyoptional.HttpServer
95462 org.jetbrains.jps.cmdline.Launcher
56766
95599 sun.tools.jps.Jps
jinfo
# 格式
jinfo [options]
# 参数
-flag 输出命名的JVM选项的值
-flag [+|-] 启用或禁用命名的JVM选项
-flag = 将命名的JVM选项设置为给定值
-flags 输出VM options
-sysprops 输出Java system properties
输出以上两种信息
# 示例
> $ jinfo -flag UseG1GC 95463
-XX:-UseG1GC
> $ jinfo -flag mx 95463
no such flag 'mx'
jstat
# 格式
# 1. 输出jstat支持的选项
jstat -options
# 在interval指定的毫秒时间内输出count次GC信息
jstat [options] pid [ []]
# 示例
> $ jstat -options
-class 类加载信息统计
-compiler 即时编译器相关信息统计
★ -gc 堆内存信息
★ -gccapacity GC分代容量信息
-gccause 输入最近两次GC产生的原因,其它同-gcutil
-gcmetacapacity 元空间大小统计
-gcnew 新生代信息统计
-gcnewcapacity 新生代大小统计
-gcold 老年代信息统计
-gcoldcapacity 老年代大小统计
★ -gcutil GC分代区域的使用率(utilization)统计
-printcompilation 输出JVM编译统计信息
> $ jstat -gc 97305
S0C 21504.0 survivor0的容量,单位KB
S1C 21504.0 survivor1的容量,单位KB
S0U 0.0 survivor0的使用量,单位KB
S1U 0.0 survivor1的使用量,单位KB
EC 131584.0 Eden的容量,单位KB
EU 13158.9 Eden的使用量,单位KB
OC 349696.0 OldGen的容量,单位KB
OU 0.0 OldGen的使用量,单位KB
MC 4480.0 Metaspace的容量,单位KB
MU 774.3 Metaspace的使用量,单位KB
CCSC 384.0 Compressed Class Space容量,单位KB(-XX:UseCompressedClassPointers)
CCSU 75.9 Compressed Class Space使用量,单位KB
YGC 0 Young GC次数
YGCT 0.000 ★ Young GC消耗的总时间
FGC 0 Full Gc次数
FGCT 0.000 ★ Full GC消耗的总时间
GCT 0 垃圾收集消耗的总时间
> $ jstat -gcutil 97305
S0 0.00 survivor0的使用率
S1 0.00 survivor1的使用率
E 10.00 Eden的使用率
O 0.00 OldGen的使用率
M 17.28 Metaspace的使用率
CCS 19.76 Compressed Class Space使用率
YGC 0 Young GC次数
YGCT 0.000 ★ Young GC消耗的总时间
FGC 0 Full Gc次数
FGCT 0.000 ★ Full GC消耗的总时间
GCT 0.000 垃圾收集消耗的总时间
jmap
# 格式
jmap [options]
# 参数
-heap 输出Java Heap概览
-histo[:live] 输出类占比情况,直方图(live表示存活对象) # histogram
-dump:live,format=b,file=xxx.hprof dump堆内存
# 示例
> $ jmap -heap 97305
Attaching to process ID 97305, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.111-b14
using thread-local object allocation.
Parallel GC with 4 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 536870912 (512.0MB)
NewSize = 178782208 (170.5MB)
MaxNewSize = 178782208 (170.5MB)
OldSize = 358088704 (341.5MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 134742016 (128.5MB)
used = 13474664 (12.850440979003906MB)
free = 121267352 (115.6495590209961MB)
10.000343174322106% used
From Space:
capacity = 22020096 (21.0MB)
used = 0 (0.0MB)
free = 22020096 (21.0MB)
0.0% used
To Space:
capacity = 22020096 (21.0MB)
used = 0 (0.0MB)
free = 22020096 (21.0MB)
0.0% used
PS Old Generation
capacity = 358088704 (341.5MB)
used = 0 (0.0MB)
free = 358088704 (341.5MB)
0.0% used
2299 interned Strings occupying 161976 bytes.
> $ jmap -dump:live,format=b,file=heap.hprof 97305
output omitted...
jstack
# 格式
jstack [options]
# 参数
-F 强制执行 thread dump,可在JVM进程hung住时使用,此选项可能需要系统权限。
-m 混合模式(mixed mode),将 Java 帧和 native 帧一起输出,此选项可能需要系统权限。
-l 长列表模式,将线程相关的 locks 信息一起输出,比如持有的锁,等待的锁。
# 示例
> $ jstack -l 97305
output omitted...
GC日志解读
ParallelGC
ParallelGC主要关注吞吐量,其在年轻代使用标记-复制
算法,在老年代使用标记-清除-整理
算法。
ParallelGC不管是在年轻代还是在老年代执行垃圾回收时都会触发STW事件——先暂停所有的应用线程,再来执行垃圾收集。在执行标记
和复制/整理
阶段时都使用多个线程,因此得名“ Parallel ”。
[GC (Allocation Failure) [PSYoungGen: 71654K->5112K(138240K)] 107258K->64868K(225792K), 0.0349397 secs] [Times: user=0.02 sys=0.04, real=0.03 secs]
[Full GC (Ergonomics) [PSYoungGen: 5112K->0K(138240K)] [ParOldGen: 59756K->61874K(129024K)] 64868K->61874K(267264K), [Metaspace: 3403K->3403K(1056768K)], 0.0250805 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
Heap
PSYoungGen total 288256K, used 91867K [0x0000000795580000, 0x00000007b4d80000, 0x00000007c0000000)
eden space 233472K, 15% used [0x0000000795580000,0x00000007979b95e0,0x00000007a3980000)
from space 54784K, 99% used [0x00000007a3980000,0x00000007a6efd810,0x00000007a6f00000)
to space 83968K, 0% used [0x00000007afb80000,0x00000007afb80000,0x00000007b4d80000)
ParOldGen total 193536K, used 138097K [0x0000000740000000, 0x000000074bd00000, 0x0000000795580000)
object space 193536K, 71% used [0x0000000740000000,0x00000007486dc448,0x000000074bd00000)
Metaspace used 3924K, capacity 4572K, committed 4864K, reserved 1056768K
class space used 439K, capacity 460K, committed 512K, reserved 1048576K
Young GC
GC (Allocation Failure)
Eden区无法分配出足够空间,导致 Young GC。
PSYoungGen: 71654K->5112K(138240K)
-
PSYoungGen
,垃圾收集器的名称,此为 Parallel Scavenge -
71654K->5112K(138240K)
,GC前后新生代的使用量以及总容量
107258K->64868K(225792K)
GC前后堆的使用量以及总容量。
0.0349397 secs
本次Young GC耗时。
Times: user=0.02 sys=0.04, real=0.03 secs
GC事件的持续时间,通过三个部分来衡量:
-
user
表示GC线程所消耗的总CPU时间 -
sys
表示操作系统调用和系统等待事件所消耗的时间 -
real
则表示应用程序实际暂停的时间。注意并不是所有的操作都能并行,所以在 ParallelGC 中,real 约等于 user + system / GC线程数
总结
基础信息:
- 年轻代总容量 138240K
- 堆内存总容量 225792K
- 老年代总容量 225792K - 138240K = 87552K
GC前:
- 年轻代使用量 71654K,则使用率为 71654K / 138240K ≈ 51.83%
- 堆内存使用量 107258K,则使用率为 107258K / 225792K ≈ 47.5%
- 老年代使用量 107258K - 71654K = 35604K,则使用率 35604K / 87552K ≈ 40.67%
GC后:
- 年轻代使用量 5112K,则使用率为 5112K / 138240K ≈ 3.7%
- 堆内存使用量 64868K,则使用率为 64868K / 225792K ≈ 28.73%
- 老年代使用量 64868K - 5112K = 59756K,则使用率为 59756K / 87552K ≈ 68.25%
GC前后:
- 年轻代减少了 71654K - 5112K = 66442K
- 堆内存减少了 107258K - 64868K = 42390K
- 老年代晋升了 66442K - 42390K = 24052K
对于 Young GC,我们比较关注暂停时间,以及GC后内存使用率是否正常,但不用特别关注GC前的使用量。因为只要业务在运转,年轻代上的对象分配就少不了,回收量也就不会少。
Full GC
Full GC (Ergonomics)
堆内存无法分配出足够空间,导致 Full GC。
ParOldGen: 59756K->61874K(129024K)
-
ParOldGen
,垃圾收集器的名称,此为 Parallel Old -
59756K->61874K(129024K)
,GC前后老年代的使用量以及总容量
Metaspace: 3403K->3403K(1056768K)
-
Metaspace
,元空间回收信息 -
3403K->3403K(1056768K)
,GC前后元空间的使用量以及总容量。可以看出,此次GC没有回收元空间中的任何对象
总结
基础信息:
- 年轻代总容量 138240K
- 堆内存总容量 267264K
- 老年代总容量 129024K
GC前:
- 年轻代使用量 5112K,则使用率为 5112K / 138240K ≈ 3.7%
- 堆内存使用量 64868K,则使用率为 64868K / 225792K ≈ 24.27%
- 老年代使用量 59756K,则使用率 59756K / 129024K ≈ 46.31%
GC后:
- 年轻代使用量 0K,则使用率为 0.0%
- 堆内存使用量 61874K,则使用率为 61874K / 225792K ≈ 27.4%
- 老年代使用量 61874K,则使用率为 61874K / 129024K ≈ 47.96%
GC前后:
- 年轻代减少了 5112K
- 堆内存减少了 64868K - 61874K = 2994K
- 老年代增加了 61874K - 59756K = 2118K,也等于 5112K - 2994K
对于 Full GC,我们更关注老年代的使用量有没有下降,以及下降了多少。如果 Full GC 之后内存不怎么下降,使用率还很高,那就说明系统可能存在问题。
CMS
CMS主要关注响应时间,其设计目标是避免在老年代GC时出现长时间的卡顿。
0.888: [GC (Allocation Failure) 0.889: [ParNew: 157248K->17472K(157248K), 0.1079694 secs] 315434K->217256K(506816K), 0.1082087 secs] [Times: user=0.15 sys=0.03, real=0.10 secs]
0.997: [GC (CMS Initial Mark) [1 CMS-initial-mark: 199784K(349568K)] 217328K(506816K), 0.0005474 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.998: [CMS-concurrent-mark-start]
1.018: [CMS-concurrent-mark: 0.020/0.020 secs] [Times: user=0.02 sys=0.01, real=0.02 secs]
1.018: [CMS-concurrent-preclean-start]
1.020: [CMS-concurrent-preclean: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
1.020: [CMS-concurrent-abortable-preclean-start]
1.045: [GC (Allocation Failure) 1.045: [ParNew: 157248K->17472K(157248K), 0.0687054 secs] 357032K->263652K(506816K), 0.0689870 secs] [Times: user=0.03 sys=0.02, real=0.07 secs]
1.237: [GC (Allocation Failure) 1.237: [ParNew: 157248K->17471K(157248K), 0.0859219 secs]1.323 403428K->303737K(506816K): [CMS-concurrent-abortable-preclean: 0.006/0.304 secs], 0.0860635 secs] [Times: user=0.23 sys=0.06, real=0.30 secs]
[Times: user=0.04 sys=0.03, real=0.09 secs]
1.324: [GC (CMS Final Remark) [YG occupancy: 17551 K (157248 K)]1.324: [Rescan (parallel) , 0.0011035 secs]1.325: [weak refs processing, 0.0000326 secs]1.325: [class unloading, 0.0010306 secs]1.326: [scrub symbol table, 0.0010828 secs]1.327: [scrub string table, 0.0006767 secs][1 CMS-remark: 286265K(349568K)] 303817K(506816K), 0.0044306 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
1.328: [CMS-concurrent-sweep-start]
1.331: [CMS-concurrent-sweep: 0.003/0.003 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
1.331: [CMS-concurrent-reset-start]
1.332: [CMS-concurrent-reset: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
1.366: [GC (Allocation Failure) 1.366: [ParNew: 157247K->17472K(157248K), 0.0199937 secs] 404523K->306245K(506816K), 0.0201222 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]
Heap
par new generation total 157248K, used 22285K [0x00000007a0000000, 0x00000007aaaa0000, 0x00000007aaaa0000)
eden space 139776K, 3% used [0x00000007a0000000, 0x00000007a04b37e0, 0x00000007a8880000)
from space 17472K, 100% used [0x00000007a8880000, 0x00000007a9990000, 0x00000007a9990000)
to space 17472K, 0% used [0x00000007a9990000, 0x00000007a9990000, 0x00000007aaaa0000)
concurrent mark-sweep generation total 349568K, used 288773K [0x00000007aaaa0000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3915K, capacity 4572K, committed 4864K, reserved 1056768K
class space used 439K, capacity 460K, committed 512K, reserved 1048576K
Young GC
Young GC 信息和使用 ParallelGC 时一致,除了 CMS 默认搭配 ParNew 进行年轻代垃圾回收而不是 Parallel Scavenge。
Full GC
阶段1:Initial Mark
初始标记伴随着STW,它的目标是标记所有的GC ROOT,包括 GC ROOT 直接引用的对象,以及被年轻代中所有存活对象所引用的对象。
-
1 CMS-initial-mark: 199784K(349568K)
,老年代的使用量以及总容量 -
217328K(506816K)
,堆内存的使用量以及总容量 -
0.0005474 secs
,初始标记耗时,可以看到非常少,因为 GC ROOT 不会特别多
阶段2:Concurrent Mark
在并发标记阶段,CMS 从前一阶段 Initial Mark
找到的 ROOT 开始算起,遍历老年代并标记所有的存活对象。
-
CMS-concurrent-mark: 0.020/0.020 secs
,GC线程消耗的时间和实际消耗的时间
阶段3:Concurrent Preclean
并发预清理同样是与应用线程并发执行的,不需要暂停应用线程。
-
CMS-concurrent-preclean: 0.001/0.001 secs
,GC线程消耗的时间和实际消耗的时间
阶段4:Concurrent Abortable Preclean
此阶段也不停止应用线程,它会尽力地在Final Remark
开始之前多干一些活。
-
CMS-concurrent-abortable-preclean: 0.006/0.304 secs
,GC线程消耗的时间和实际消耗的时间
阶段5:Final Remark
最终标记是此次GC事件中的第二次(也是最后一次)STW停顿。 本阶段的目标是完成老年代中所有存活对象的标记,因为之前的预清理阶段是并发执行的,有可能GC线程跟不上应用程序的修改速度,所以需要一次 STW 暂停来处理各种复杂的情况。 通常CMS会尝试在年轻代尽可能空的情况下执行Final Remark
,以免连续触发多次 STW 事件。
-
YG occupancy: 17551 K (157248 K)
,年轻代的使用量和总容量 -
Rescan (parallel) , 0.0011035 secs
,在程序暂停后进行重新扫描,以完成存活对象的标记。这部分是并行执行的,消耗的时间为 0.0011035 秒 -
weak refs processing, 0.0000326 secs]
,第一个子阶段,处理弱引用的耗时 -
class unloading, 0.0010306 secs
,第二个子阶段,卸载类的耗时 -
scrub symbol table, 0.0010828 secs
,第三个子阶段,清理符号表,即持有 class 级别 metadata 的符号表(symbol tables) -
scrub string table, 0.0006767 secs
,第四个子阶段,清理内联字符串对应的 string tables -
1 CMS-remark: 286265K(349568K)
,此阶段完成后老年代的使用量和总容量 -
303817K(506816K)
,此阶段完成后堆内存的使用量和总容量 -
0.0044306 secs
,此阶段消耗的时间
在这5个标记阶段完成后,老年代中的所有存活对象都被标记上了,接下来 JVM 会清除所有不使用的对象,以回收老年代空间。
阶段6:Concurrent Sweep
此阶段与应用程序并发执行,不需要STW停顿,目的是删除不再使用的对象,并回收它们占用的内存空间。
-
CMS-concurrent-sweep: 0.003/0.003 secs
,GC线程消耗的时间和实际消耗的时间
阶段7:Concurrent Reset
此阶段与应用程序线程并发执行,重置CMS算法相关的内部数据结构,为下一次GC做准备。
-
CMS-concurrent-reset: 0.001/0.001 secs
,GC线程消耗的时间和实际消耗的时间
虽然 CMS 日志比较详细,但却不好计算其触发GC以后老年代的使用量,这个问题可以根据之后触发的一次 Young GC 来计算。