G1
垃圾收集器的设计原则是“首先收集尽可能多的垃圾(Garbage First)”,目标是为了尽量缩短处理超大堆(超过4GB)产生的停顿。
因此, G1
并不会等内存耗尽(比如 Serial
串行收集器、 Parallel
并行收集器 )者快耗尽( CMS
)的时候才开始垃圾回收,而是在内部采用了启发式算法,在老年代中找出具有高收集收益的分区( Region
)进行收集。
同时 G1
可以根据用户设置的 STW
(Stop-The-World)停顿时间目标(响应时间优先)自动调整年轻代和总堆的大小,停顿时间越短年轻代空间就可能越小,总堆空间越大。
G1
相对于 CMS
一个比较明显的优势是,内存碎片的产生率大大降低。
G1
在 JDK7u4以上都可以使用,在JDK9开始, G1
为默认的垃圾收集器,以替代 CMS
。
算法:三色标记 + SATB
STW
(Stop-The-World)停顿时间。Region
)。Remembered Set
来避免全堆扫描。
传统的GC收集器将连续的内存空间划分为新生代、老年代和永久代(JDK 8去除了永久代,引入了元空间Metaspace),
这种划分的特点是各代的存储地址(逻辑地址,下同)是连续的。如下图所示:
而G1的各代存储地址是不连续的,每一代都使用了n个不连续的大小相同的 Region
,每个 Region
占有一块连续的虚拟内存地址。
Region
(区域,分区)G1采用了分区( Region
)的思路,将整个堆空间分成若干个大小相等的内存区域,每次分配对象空间将逐段地使用内存。
虽然还保留了新生代和老年代的概念,但新生代和老年代不再是物理隔离,它们都是一部分 Region
(不需要连续)的集合。
因此,在堆的使用上,G1并不要求对象的存储一定是物理上连续的,只要逻辑上连续即可;
每个分区 Region
也不会确定地为某个代服务,可以按需在年轻代和老年代之间切换。
启动时可以通过参数 -XX:G1HeapRegionSize=n
可指定分区大小(1MB~32MB,且必须是2的幂),默认将整堆划分为2048个分区。
Card
(卡片)在每个分区 Region
内部又被分成了若干个大小为512 Byte卡片(Card),标识堆内存最小可用粒度。
所有分区 Region
的卡片将会记录在全局卡片表( Global Card Table
)中。
分配的对象会占用物理上连续的若干个卡片。
当查找对分区 Region
内对象的引用时便可通过记录卡片来查找该引用对象(见 RSet
)。
每次对内存的回收,都是对指定分区的卡片进行处理。
Heap
(堆)G1同样可以通过 -Xms/-Xmx
来指定堆空间大小。
当发生年轻代收集( YGC
)或混合收集( Mixed GC
)时,通过计算GC与应用的耗费时间比,自动调整堆空间大小。
如果GC频率太高,则通过增加堆尺寸,来减少GC频率,相应地GC占用的时间也随之降低;
目标参数 -XX:GCTimeRatio
即为GC与应用的耗费时间比,G1默认为9,而CMS默认为99,因为CMS的设计原则是耗费在GC上的时间尽可能的少。
另外,当空间不足,如对象空间分配或转移失败时,G1会首先尝试增加堆空间,如果扩容失败,则发起担保的 Full GC
。
Full GC
后,堆尺寸计算结果也会调整堆空间。
Generation
(分代 )分代垃圾收集可以将关注点集中在最近被分配的对象上,而无需整堆扫描,避免长命对象的拷贝,同时独立收集有助于降低响应时间。
虽然分区使得内存分配不再要求紧凑的内存空间,但G1依然使用了分代的思想。
与其他垃圾收集器类似,G1将内存在逻辑上划分为年轻代和老年代,其中年轻代又划分为Eden空间和Survivor空间。
但年轻代空间并不是固定不变的,当现有年轻代分区占满时,JVM会分配新的空闲分区加入到年轻代空间。
整个年轻代内存会在初始空间 -XX:NewSize
与最大空间 -XX:MaxNewSize
之间动态变化,且由参数目标暂停时间 -XX:MaxGCPauseMillis
、需要扩缩容的大小以及分区的已记忆集合( RSet
)计算得到。
当然,G1依然可以设置固定的年轻代大小(参数
-XX:NewRatio
、-Xmn
),但同时暂停目标将失去意义。
##### Local allocation buffer
( LAB
) (本地分配缓冲) 值得注意的是,由于分区的思想,每个线程均可以"认领"某个分区 Region
用于线程本地的内存分配,而不需要顾及分区是否连续。
因此,每个应用线程和GC线程都会独立的使用分区,进而减少同步时间,提升GC效率,这个分区 Region
称为本地分配缓冲区( LAB
)。
TLAB
: 应用线程可以独占一个本地缓冲区( TLAB
)来创建的对象,而大部分都会落入Eden区域(巨型对象或分配失败除外),因此TLAB的分区属于Eden空间;GCLAB
: 每次垃圾收集时,每个GC线程同样可以独占一个本地缓冲区( GCLAB
)用来转移对象,每次回收会将对象复制到Suvivor空间或老年代空间;PLAB
: 对于从Eden/Survivor空间晋升(Promotion)到Survivor/老年代空间的对象,同样有GC独占的本地缓冲区进行操作,该部分称为晋升本地缓冲区( PLAB
)。Humongous Object
(巨型对象)一个大小达到甚至超过分区 Region
50%以上的对象称为巨型对象( Humongous Object
)。 巨型对象会独占一个、或多个连续分区,其中第一个分区被标记为开始巨型( StartsHumongous
),相邻连续分区被标记为连续巨型( ContinuesHumongous
)。 Humongous Object
有以下特点:
Humongous Object
直接分配到了 老年代,防止了反复拷贝移动。
当线程为巨型分配空间时,不能简单在
TLAB
进行分配,因为巨型对象的移动成本很高,而且有可能一个分区不能容纳巨型对象。 因此,巨型对象会直接在老年代分配,所占用的连续空间称为巨型分区(Humongous Region
)。
Humongous Object
在 YGC
阶段, Global Concurrent Marking
阶段的 Cleanup
和 FGC
阶段 回收。
由于无法享受
LAB
带来的优化,并且确定一片连续的内存空间需要扫描整堆Heap
,因此确定巨型对象开始位置的成本非常高,如果可以,应用程序应避免生成巨型对象。
在分配 Humongous Object
之前先检查是否超过 initiating heap occupancy percent (由参数 -XX:InitiatingHeapOccupancyPercent
控制) 和 the marking threshold。 如果超过的话,就启动并发收集周期 Concurrent Marking Cycle
,为的是提早回收,防止 Evacuation Failure
和 Full GC
。
RSet
( Remember Set
,已记忆集合)在串行和并行收集器中,GC通过整堆扫描,来确定对象是否处于可达路径中。
然而G1为了避免 STW
式的整堆 Heap
扫描,在每个分区 Region
记录了一个已记忆集合( RSet
),内部类似一个反向指针,记录引用分区 Region
内对象的卡片 Card
的索引。
当要回收该分区 Region
时,通过扫描分区的RSet,来确定引用本分区内的对象是否存活,进而确定本分区内的对象存活情况。
事实上,并非所有的引用都需要记录在 RSet
中,如果一个分区 Region
确定需要扫描,那么无需 RSet
也可以无遗漏的得到引用关系。
那么引用源自本分区 Region
的对象,当然不用落入 RSet
中;
同时,G1 GC每次都会对年轻代进行整体收集,因此引用源自年轻代的对象,也不需要在 RSet
记录。
最后只有老年代的分区 Region
可能会有RSet记录,这些分区称为拥有RSet分区(an RSet’s owning region)。
Per Region Table
(PRT) RSet
在内部使用 Per Region Table
(PRT)记录分区 Region
的引用情况。 由于 RSet
的记录要占用分区 Region
的空间,如果一个分区非常"受欢迎",那么 RSet
占用的空间会上升,从而降低分区 Region
的可用空间。 G1应对这个问题采用了改变 RSet
的密度的方式,在 PRT
中将会以三种模式记录引用:
Card
的索引Region
的索引由上可知,粗粒度的 PRT
只是记录了引用数量,需要通过整堆 Heap
扫描才能找出所有引用,因此扫描速度也是最慢的。
CSet
( Collection Set
,收集集合)收集集合( CSet
)代表每次GC暂停时回收的一系列目标分区 Region
。
在任意一次收集暂停中, CSet
所有分区都会被释放,内部存活的对象都会被转移到分配的空闲分区中。
因此无论是年轻代收集,还是混合收集,工作的机制都是一致的。
年轻代收集( YGC
)的 CSet
只容纳年轻代分区,而混合收集( Mixed GC
)会通过启发式算法,在老年代候选回收分区中,筛选出回收收益最高的分区添加到 CSet
中。
候选老年代分区的 CSet
准入条件,可以通过活跃度阈值 -XX:G1MixedGCLiveThresholdPercent
(默认85%)进行设置,从而拦截那些回收开销巨大的对象;
同时,每次混合收集可以包含候选老年代分区,可根据 CSet
对堆的总大小占比 -XX:G1OldCSetRegionThresholdPercent
(默认10%)设置数量上限。
由上述可知,G1的收集都是根据 CSet
进行操作的,年轻代收集( YGC
)与混合收集( Mixed GC
)没有明显的不同,最大的区别在于两种收集的触发条件。
CSet of Young Collection
应用线程不断活动后,年轻代空间会被逐渐填满。当JVM分配对象到Eden区域失败(Eden区已满)时,便会触发一次 STW
式的年轻代收集。 在年轻代收集中,Eden分区存活的对象将被拷贝到Survivor分区; 原有Survivor分区存活的对象,将根据任期阈值(tenuring threshold)分别晋升到 PLAB
中,新的survivor分区和老年代分区。而原有的年轻代分区将被整体回收掉。
同时,年轻代收集还负责维护对象的年龄(存活次数),辅助判断老化(tenuring)对象晋升的时候是到Survivor分区还是到老年代分区。 年轻代收集首先先将晋升对象尺寸总和、对象年龄信息维护到年龄表中,再根据年龄表、Survivor尺寸、Survivor填充容量 -XX:TargetSurvivorRatio
(默认50%)、最大任期阈值 -XX:MaxTenuringThreshold
(默认15),计算出一个恰当的任期阈值,凡是超过任期阈值的对象都会被晋升到老年代。
CSet of Mixed Collection
年轻代收集不断活动后,老年代的空间也会被逐渐填充。当老年代占用空间超过整堆比IHOP阈值 -XX:InitiatingHeapOccupancyPercent
(默认45%)时,G1就会启动一次混合垃圾收集周期。
为了满足暂停目标,G1可能不能一口气将所有的候选分区收集掉,因此G1可能会产生连续多次的混合收集与应用线程交替执行,每次 STW
的混合收集与年轻代收集过程相类似。
-XX:G1MixedGCCountTarget
(默认8)、堆废物百分比 -XX:G1HeapWastePercent
(默认5%)。通过候选老年代分区总数与混合周期最大总次数,确定每次包含到 CSet
的最小分区数量;
根据堆废物百分比,当收集达到参数时,不再启动新的混合收集。而每次添加到 CSet
的分区,则通过计算得到的GC效率进行安排。
G1的垃圾回收包括了以下几种:
Concurrent Marking Cycle (并发收集) 类似 CMS
的并发收集过程。
Young Collection (YGC,年轻代收集, STW
)
Mixed Collection Cycle (混合收集, STW
)
Full GC(FGC, STW
)
JDK10以前FGC是串行回收,JDK10+可以是并行回收。*
Concurrent Marking Cycle
并发标记周期是G1中非常重要的阶段,这个阶段将会为混合收集周期识别垃圾最多的老年代分区。
整个周期完成根标记、识别所有(可能)存活对象,并计算每个分区的活跃度,从而确定GC效率等级。
当达到IHOP阈值 -XX:InitiatingHeapOccupancyPercent
(老年代占整堆比,默认45%)时,便会触发并发标记周期。
整个并发标记周期将由初始标记(Initial Mark)、根分区扫描(Root Region Scanning)、并发标记(Concurrent Marking)、重新标记(Remark)、清除(Cleanup)几个阶段组成。
其中,初始标记(随年轻代收集一起活动)、重新标记、清除是STW的, 而并发标记如果来不及标记存活对象,则可能在并发标记过程中,G1又触发了几次年轻代收集( YGC
)。
Initial Marking
(初始标记, STW)它标记了从GC Root开始直接可达的对象。
事实上,当达到IHOP阈值时,G1并不会立即发起并发标记周期,而是等待下一次年轻代收集,利用年轻代收集的STW时间段,完成初始标记,这种方式称为借道(Piggybacking)。
Root region scanning
(根分区扫描)在初始标记暂停结束后,年轻代收集也完成的对象复制到Survivor的工作,应用线程开始活跃起来。此时为了保证标记算法的正确性,所有新复制到Survivor分区的对象,都需要被扫描并标记成根,这个过程称为根分区扫描(Root Region Scanning),同时扫描的Suvivor分区也被称为根分区(Root Region)。
Concurrent Marking
(并发标记)这个阶段从GC Root开始对heap中的对象标记,标记线程与应用程序线程并行执行,并且收集各个 Region
的存活对象信息。 和应用线程并发执行,并发标记线程在并发标记阶段启动,由参数 -XX:ConcGCThreads
(默认GC线程数的1/4,即-XX:ParallelGCThreads/4)控制启动数量, 每个线程每次只扫描一个分区 Region
,从而标记出存活对象图。
所有的标记任务必须在堆满前就完成扫描,如果并发标记耗时很长,那么有可能在并发标记过程中,又经历了几次年轻代收集。 如果堆满前没有完成标记任务,则会触发担保机制,经历一次长时间的串行Full GC。
Remark
( 重新标记,STW)标记那些在并发标记阶段发生变化的对象,将被回收。 这个阶段也是并行执行的,通过参数 -XX:ParallelGCThread
可设置GC暂停时可用的GC线程数。
Cleanup
(清理,STW)清除阶段主要执行以下操作:
RSet
梳理,启发式算法会根据活跃度和 RSet
尺寸对分区定义不同等级,同时 RSet
数理也有助于发现无用的引用。参数 -XX:+PrintAdaptiveSizePolicy
可以开启打印启发式算法决策细节;当应用运行开始时,堆内存可用空间还比较大,只会在年轻代满时,触发年轻代收集;
随着老年代内存增长,当到达IHOP阈值 -XX:InitiatingHeapOccupancyPercent
(老年代占整堆比,默认45%)时,G1开始着手准备收集老年代空间。
首先经历并发标记周期 Concurrent Marking Cycle
,识别出高收益的老年代分区,前文已述。
但随后G1并不会马上开始一次混合收集,而是让应用线程先运行一段时间,等待触发一次年轻代收集。
在这次STW中,G1将保准整理混合收集周期。接着再次让应用线程运行,当接下来的几次年轻代收集时,将会有老年代分区加入到CSet中,
即触发混合收集,这些连续多次的混合收集称为混合收集周期( Mixed Collection Cycle
)。
YGC
每次收集过程中,既有并行执行的活动,也有串行执行的活动,但都可以是多线程的。
在并行执行的任务中,如果某个任务过重,会导致其他线程在等待某项任务的处理,需要对这些地方进行优化。
以下部分部分可以结合日志查看
并行活动
外部根分区扫描 Ext Root Scanning: 此活动对堆外的根(JVM系统目录、VM数据结构、JNI线程句柄、硬件寄存器、全局变量、线程对栈根)进行扫描,发现那些没有加入到暂停收集集合CSet中的对象。如果系统目录(单根)拥有大量加载的类,最终可能其他并行活动结束后,该活动依然没有结束而带来的等待时间。
更新已记忆集合 Update RS: 并发优化线程会对脏卡片的分区进行扫描更新日志缓冲区来更新RSet,但只会处理全局缓冲列表。作为补充,所有被记录但是还没有被优化线程处理的剩余缓冲区,会在该阶段处理,变成已处理缓冲区(Processed Buffers)。为了限制花在更新RSet的时间,可以设置暂停占用百分比 -XX:G1RSetUpdatingPauseTimePercent
(默认10%,即-XX:MaxGCPauseMills/10)。值得注意的是,如果更新日志缓冲区更新的任务不降低,单纯地减少RSet的更新时间,会导致暂停中被处理的缓冲区减少,将日志缓冲区更新工作推到并发优化线程上,从而增加对Java应用线程资源的争夺。
RSet扫描 Scan RS: 在收集当前CSet之前,考虑到分区外的引用,必须扫描CSet分区的RSet。如果RSet发生粗化,则会增加RSet的扫描时间。 开启诊断模式 -XX:UnlockDiagnosticVMOptions
后, 通过参数 -XX:+G1SummarizeRSetStats
可以确定并发优化线程是否能够及时处理更新日志缓冲区,并提供更多的信息,来帮助为RSet粗化总数提供窗口。 参数 -XX:G1SummarizeRSetStatsPeriod=n
可设置RSet的统计周期,即经历多少此GC后进行一次统计
代码根扫描 Code Root Scanning:对代码根集合进行扫描,扫描JVM编译后代码Native Method的引用信息(nmethod扫描),进行RSet扫描。事实上,只有CSet分区中的RSet有强代码根时,才会做nmethod扫描,查找对CSet的引用。
转移和回收 Object Copy: 通过选定的CSet以及CSet分区完整的引用集,将执行暂停时间的主要部分:CSet分区存活对象的转移、CSet分区空间的回收。通过工作窃取机制来负载均衡地选定复制对象的线程,并且复制和扫描对象被转移的存活对象将拷贝到每个GC线程分配缓冲区GCLAB。G1会通过计算,预测分区复制所花费的时间,从而调整年轻代的尺寸。
终止 Termination: 完成上述任务后,如果任务队列已空,则工作线程会发起终止要求。如果还有其他线程继续工作,空闲的线程会通过工作窃取机制尝试帮助其他线程处理。而单独执行根分区扫描的线程,如果任务过重,最终会晚于终止。
GC外部的并行活动 GC Worker Other: 该部分并非GC的活动,而是JVM的活动导致占用了GC暂停时间(例如JNI编译)。
串行活动
代码根更新 Code Root Fixup:根据转移对象更新代码根。
代码根清理 Code Root Purge:清理代码根集合表。
清除全局卡片标记 Clear CT:在任意收集周期会扫描CSet与RSet记录的PRT,扫描时会在全局卡片表中进行标记,防止重复扫描。在收集周期的最后将会清除全局卡片表中的已扫描标志。
选择下次收集集合 Choose CSet:该部分主要用于并发标记周期后的年轻代收集、以及混合收集中,在这些收集过程中,由于有老年代候选分区的加入,往往需要对下次收集的范围做出界定;但单纯的年轻代收集中,所有收集的分区都会被收集,不存在选择。
引用处理 Ref Proc:主要针对软引用、弱引用、虚引用、final引用、JNI引用。当Ref Proc占用时间过多时,可选择使用参数 -XX:ParallelRefProcEnabled
激活多线程引用处理。G1希望应用能小心使用软引用,因为软引用会一直占据内存空间直到空间耗尽时被Full GC回收掉;即使未发生Full GC,软引用对内存的占用,也会导致GC次数的增加。
引用排队 Ref Enq:此项活动可能会导致RSet的更新,此时会通过记录日志,将关联的卡片标记为脏卡片。
卡片重新脏化 Redirty Cards:重新脏化卡片。
回收空闲巨型分区 Humongous Reclaim:G1做了一个优化:通过查看所有根对象以及年轻代分区的RSet,如果确定RSet中巨型对象没有任何引用,则说明G1发现了一个不可达的巨型对象,该对象分区会被回收。
释放分区 Free CSet:回收CSet分区的所有空间,并加入到空闲分区中。
其他活动 Other:GC中可能还会经历其他耗时很小的活动,如修复JNI句柄等。
当G1发起并发标记周期之后,并不会马上开始混合收集。 G1会先等待下一次年轻代收集,然后在该收集阶段中,确定下次混合收集的CSet(Choose CSet)。
Mixed GC
单次的混合收集与年轻代收集并无二致。
根据暂停目标,老年代的分区可能不能一次暂停收集中被处理完,G1会发起连续多次的混合收集,称为混合收集周期(Mixed Collection Cycle)。
G1会计算每次加入到CSet中的分区数量、混合收集进行次数,并且在上次的年轻代收集、以及接下来的混合收集中,G1会确定下次加入CSet的分区集(Choose CSet),并且确定是否结束混合收集周期。
Full GC
转移失败( Evacuation Failure
)是指当G1无法在堆空间中申请新的分区时,G1便会触发担保机制,执行一次 STW
式的、单线程(JDK10支持多线程)的Full GC。
Full GC会对整堆做标记清除和压缩,最后将只包含纯粹的存活对象。参数 -XX:G1ReservePercent
(默认10%)可以保留空间,来应对晋升模式下的异常情况,最大占用整堆50%,更大也无意义。
G1在以下场景中会触发Full GC,同时会在日志中记录 to-space exhausted
以及 Evacuation Failure
:
Humongous Object
时在老年代无法找到足够的连续分区由于G1的应用场合往往堆内存都比较大,所以Full GC的收集代价非常昂贵,应该避免Full GC的发生。
什么时候触发concurrent marking ? ```shell
XX:InitiatingHeapOccupancyPercent ```
什么时候发生Mixed GC? concurrent marking 主要是为Mixed GC提供标记服务的,并不是一次GC过程的一个必须环节。
由一些参数控制,另外也控制着哪些老年代Region会被选入CSet(收集集合)。
# 一次 concurrent marking之后,最多执行Mixed GC的次数(默认8)
-XX:G1MixedGCCountTarget
# 堆废物百分比(默认5%),在每次YGC之后和再次发生Mixed GC之前,会检查垃圾占比是否达到此参数,只有达到了,下次才会发生Mixed GC。
-XX:G1HeapWastePercent
# old generation region中的存活对象的占比,只有在此参数之下,才会被选入CSet。
-XX:G1MixedGCLiveThresholdPercent
# 一次Mixed GC中能被选入CSet的最多old generation region数量。
-XX:G1OldCSetRegionThresholdPercent复制代码
[GC concurrent-root-region-scan-start]
[GC concurrent-root-region-scan-end, 0.0094252 secs]
# 根分区扫描,可能会被 YGC 打断,那么结束就是如:[GC pause (G1 Evacuation Pause) (young)[GC concurrent-root-region-scan-end, 0.0007157 secs]
[GC concurrent-mark-start]
[GC concurrent-mark-end, 0.0203881 secs]
# 并发标记阶段
[GC remark [Finalize Marking, 0.0007822 secs] [GC ref-proc, 0.0005279 secs] [Unloading, 0.0013783 secs], 0.0036513 secs]
# 重新标记,STW
[Times: user=0.01 sys=0.00, real=0.00 secs]
[GC cleanup 13985K->13985K(20480K), 0.0034675 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
# 清除复制代码
[GC pause (G1 Evacuation Pause) (young), 0.0022483 secs]
# young -> 年轻代 Evacuation-> 复制存活对象
[Parallel Time: 1.0 ms, GC Workers: 10] # 并发执行的GC线程数,以下阶段是并发执行的
[GC Worker Start (ms): Min: 109.0, Avg: 109.1, Max: 109.1, Diff: 0.2]
[Ext Root Scanning (ms): Min: 0.1, Avg: 0.2, Max: 0.3, Diff: 0.2, Sum: 2.3] # 外部根分区扫描
[Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] # 更新已记忆集合 Update RSet,检测从年轻代指向老年代的对象
[Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]# RSet扫描
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1] # 代码根扫描
[Object Copy (ms): Min: 0.3, Avg: 0.3, Max: 0.4, Diff: 0.1, Sum: 3.5] # 转移和回收,拷贝存活的对象到survivor/old区域
[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] # 完成上述任务后,如果任务队列已空,则工作线程会发起终止要求。
[Termination Attempts: Min: 1, Avg: 5.8, Max: 9, Diff: 8, Sum: 58]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1] # GC外部的并行活动,该部分并非GC的活动,而是JVM的活动导致占用了GC暂停时间(例如JNI编译)。
[GC Worker Total (ms): Min: 0.5, Avg: 0.6, Max: 0.7, Diff: 0.2, Sum: 5.9]
[GC Worker End (ms): Min: 109.7, Avg: 109.7, Max: 109.7, Diff: 0.0]
[Code Root Fixup: 0.0 ms] # 串行任务,根据转移对象更新代码根
[Code Root Purge: 0.0 ms] #串行任务, 代码根清理
[Clear CT: 0.5 ms] #串行任务,清除全局卡片 Card Table 标记
[Other: 0.8 ms]
[Choose CSet: 0.0 ms] # 选择下次收集集合 CSet
[Ref Proc: 0.4 ms] # 引用处理 Ref Proc,处理软引用、弱引用、虚引用、final引用、JNI引用
[Ref Enq: 0.0 ms] # 引用排队 Ref Enq
[Redirty Cards: 0.3 ms] # 卡片重新脏化 Redirty Cards:重新脏化卡片
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms] # 回收空闲巨型分区 Humongous Reclaim,通过查看所有根对象以及年轻代分区的RSet,如果确定RSet中巨型对象没有任何引用,该对象分区会被回收。
[Free CSet: 0.0 ms] # 释放分区 Free CSet
[Eden: 12288.0K(12288.0K)->0.0B(11264.0K) Survivors: 0.0B->1024.0K Heap: 12288.0K(20480.0K)->832.0K(20480.0K)]
[Times: user=0.01 sys=0.00, real=0.00 secs] 复制代码
# 从年轻代分区拷贝存活对象时,无法找到可用的空闲分区
# 从老年代分区转移存活对象时,无法找到可用的空闲分区 这两种情况之一导致的 YGC
[GC pause (G1 Evacuation Pause) (young) (to-space exhausted), 0.0916534 secs]复制代码
# 并发标记周期 Concurrent Marking Cycle 中的 根分区扫描阶段,被 YGC中断
[GC pause (G1 Evacuation Pause) (young)[GC concurrent-root-region-scan-end, 0.0007157 secs]复制代码
# 并发标记周期 Concurrent Marking Cycle 的开始
[GC pause (G1 Evacuation Pause) (young) (initial-mark) , 0.0443460 secs]复制代码
[Full GC (Allocation Failure) 20480K->9656K(20480K), 0.0189481 secs]
[Eden: 0.0B(1024.0K)->0.0B(5120.0K) Survivors: 0.0B->0.0B Heap: 20480.0K(20480.0K)->9656.8K(20480.0K)], [Metaspace: 4960K->4954K(1056768K)]
[Times: user=0.03 sys=0.00, real=0.02 secs] 复制代码
by Sven Augustus my.oschina.net/langxSpirit
本文由博客群发一文多发等运营工具平台 OpenWrite 发布