从年轻代和老年代的collect方法的调用方出发,来总结GC是如何被触发的。
1、GenCollectedHeap::do_collection
do_collection方法是调用各Genaration的collect方法的直接上层方法,其主要流程如下:
其中计算start_level的逻辑如下:
DefNewGeneration采用父类Generation的默认实现,返回false,ConcurrentMarkSweepGeneration的实现如下,在默认配置下该方法返回true,即full为true,max_level为1的时候,start_level也为1,max_level是方法入参,表示执行GC的最大level。
//UseCMSCompactAtFullCollection默认为true,表示FullGC时所使用Mark-Sweep-Compact算法
//CollectGen0First默认为false,表示是否在FullGC前收集年轻代
virtual bool full_collects_younger_generations() const {
return UseCMSCompactAtFullCollection && !CollectGen0First;
}
其中是否complete的判断逻辑如下:
bool complete = full && (max_level == (n_gens()-1));
int max_level_collected = starting_level;
complete = complete || (max_level_collected == n_gens() - 1);
即只要max_level等于1的时候complete就为true,如果为1则为false。
补充说明如下:
2、CollectedHeap::collect_as_vm_thread
collect_as_vm_thread方法是给VMThread使用的执行GC的内部方法,该方法假定已经获取了Heap_lock锁,其实现如下:
void CollectedHeap::collect_as_vm_thread(GCCause::Cause cause) {
//校验是VMThread
assert(Thread::current()->is_VM_thread(), "Precondition#1");
//校验获取了Heap_lock
assert(Heap_lock->is_locked(), "Precondition#2");
//临时设置GCCause
GCCauseSetter gcs(this, cause);
switch (cause) {
case GCCause::_heap_inspection: //表示打印堆内存中包含的类的直方图
case GCCause::_heap_dump: //堆内存dump
case GCCause::_metadata_GC_threshold : { //表示元空间内存分配失败
HandleMark hm;
do_full_collection(false); //false表示不需要清除软引用
break;
}
case GCCause::_last_ditch_collection: { //执行过do_full_collection(false)后再次尝试从元空间分配内存,分配失败后就尝试扩容再分配,依然失败就会使用这个GCCause
HandleMark hm;
do_full_collection(true); //true表示清除软引用
break;
}
default:
ShouldNotReachHere(); // Unexpected use of this function
}
}
void GenCollectedHeap::do_full_collection(bool clear_all_soft_refs,
int max_level) {
int local_max_level;
if (!incremental_collection_will_fail(false /* don't consult_young */) &&
gc_cause() == GCCause::_gc_locker) {
local_max_level = 0;
} else {
local_max_level = max_level;
}
do_collection(true /* full */,
clear_all_soft_refs /* clear_all_soft_refs */,
0 /* size */,
false /* is_tlab */,
local_max_level /* max_level */);
if (local_max_level == 0 && gc_cause() == GCCause::_gc_locker &&
incremental_collection_will_fail(false /* don't consult_young */)) {
if (PrintGCDetails) {
gclog_or_tty->print_cr("GC locker: Trying a full collection "
"because scavenge failed");
}
//如果老年代空间不足,则回收老年代
do_collection(true /* full */,
clear_all_soft_refs /* clear_all_soft_refs */,
0 /* size */,
false /* is_tlab */,
n_gens() - 1 /* max_level */);
}
}
在collect_as_vm_thread场景下do_full_collection方法实际就只执行do_collection,其中full为true,max_level就是1,即只回收老年代。collect_as_vm_thread的调用链如下:
3、VM_CollectForMetadataAllocation
VM_CollectForMetadataAllocation是元空间内存分配失败后触发GC的实现,参考其构造方法的调用链,如下:
该类的核心doit方法的主流程如下:
补充说明如下:
4、GenCollectedHeap::collect
该方法主要用于JNI场景下触发GC,其调用链如下:
第一个方法是最后一个从JNI关键区退出的线程触发GC,调用入参是GCCause::_gc_locker,第二个是System.gc()的底层实现,调用入参是GCCause::_java_lang_system_gc,第三个方法是从命令行触发GC的实现,调用入参也是GCCause::_java_lang_system_gc,最后两个都是用于白盒测试的WhiteBox类的native方法,调用入参分别是GCCause::_last_ditch_collection和GCCause::_wb_young_gc。
该方法的实现如下:
void GenCollectedHeap::collect(GCCause::Cause cause) {
//在默认配置下,should_do_concurrent_full_gc返回false
if (should_do_concurrent_full_gc(cause)) {
collect_mostly_concurrent(cause);
} else if (cause == GCCause::_wb_young_gc) {
collect(cause, 0);
} else {
collect(cause, n_gens() - 1);
}
}
bool GenCollectedHeap::should_do_concurrent_full_gc(GCCause::Cause cause) {
//UseConcMarkSweepGC表示使用CMS GC算法,GCLockerInvokesConcurrent的默认值为false,表示从JNI关键区中退出时是否触发后台GC
//ExplicitGCInvokesConcurrent的默认值也是false,表示调用System.gc时是否触发后台GC
return UseConcMarkSweepGC &&
((cause == GCCause::_gc_locker && GCLockerInvokesConcurrent) ||
(cause == GCCause::_java_lang_system_gc && ExplicitGCInvokesConcurrent));
}
void GenCollectedHeap::collect(GCCause::Cause cause, int max_level) {
assert(!Heap_lock->owned_by_self(), "this thread should not own the Heap_lock");
//获取Heap_lock
MutexLocker ml(Heap_lock);
collect_locked(cause, max_level);
}
void GenCollectedHeap::collect_locked(GCCause::Cause cause, int max_level) {
//获取GC前的gc次数
unsigned int gc_count_before = total_collections();
unsigned int full_gc_count_before = total_full_collections();
{
//此方法通常都是JavaThread调用的,因此需要通过VM_Operation和VMThread来执行GC
//执行完大括号包含的逻辑后自动释放Heap_lock
MutexUnlocker mu(Heap_lock); // give up heap lock, execute gets it back
VM_GenCollectFull op(gc_count_before, full_gc_count_before,
cause, max_level);
VMThread::execute(&op);
}
}
实际触发GC通过VM_GenCollectFull类完成,其关键doit方法的实现如下:
void VM_GenCollectFull::doit() {
SvcGCMarker sgcm(SvcGCMarker::FULL);
GenCollectedHeap* gch = GenCollectedHeap::heap();
GCCauseSetter gccs(gch, _gc_cause);
gch->do_full_collection(gch->must_clear_all_soft_refs(), _max_level);
}
bool GenCollectedHeap::must_clear_all_soft_refs() {
return _gc_cause == GCCause::_last_ditch_collection;
}
综上,只有GCCause::_wb_young_gc触发年轻代GC,其他场景都是触发老年代GC,并且只有WB_FullGC时会清理所有的软引用,其他的都不清理。
5、mem_allocate_work
mem_allocate_work是Java堆对外内存分配的底层入口,其调用链如下:
代码实现可以参考垃圾回收之CollectorPolicy ,主要流程如下:
其中判断是否在老年代尝试内存分配的实现如下:
//如果first_only为true则只在年轻代分配,如果分配失败则直接返回NULL,不再尝试
//在老年代分配,否则会再次尝试在老年代分配
bool first_only = ! should_try_older_generation_allocation(size);
bool GenCollectorPolicy::should_try_older_generation_allocation(
size_t word_size) const {
GenCollectedHeap* gch = GenCollectedHeap::heap();
size_t gen0_capacity = gch->get_gen(0)->capacity_before_gc();
//只有这三个条件都为false,first_only才会为true,只要有一个条件为true,first_only为false
return (word_size > heap_word_size(gen0_capacity))
|| GC_locker::is_active_and_needs_gc() //如果后台GC被触发了且有线程处于JNI关键区返回true
|| gch->incremental_collection_failed(); //老年代空间不足导致前一次的年轻GC promote失败,返回true
}
即如果first_only为true时会触发年轻代GC,否则会尝试在老年代分配,如果分配失败触发老年代GC。
判断是否有扩展空间的实现如下:
//只要有一个Generation有扩展空间,则返回false,表示有扩展空间
bool GenCollectedHeap::is_maximal_no_gc() const {
for (int i = 0; i < _n_gens; i++) {
if (!_gens[i]->is_maximal_no_gc()) {
return false;
}
}
return true;
}
//返回true,表示无扩展空间,返回false表示有扩展空间
virtual bool is_maximal_no_gc() const {
//uncommitted_size表示还未向操作系统申请分配内存的容量
return _virtual_space.uncommitted_size() == 0;
}
补充说明如下:
mem_allocate_work判断该属性为true后终止循环,通知调用方次数超了后将该属性重置为false,最后返回NULL,外层调用方识别full GC次数超了依然分配失败抛出异常。
6、VM_GenCollectForAllocation
VM_CollectForMetadataAllocation是从元空间分配内存失败时触发GC的实现,VM_GenCollectForAllocation与之对应,是从Java堆分配内存失败触发GC的实现,其调用链如下:
其核心doit方法的实现如下:
void VM_GenCollectForAllocation::doit() {
SvcGCMarker sgcm(SvcGCMarker::MINOR);
GenCollectedHeap* gch = GenCollectedHeap::heap();
//临时设置gc_cause属性
GCCauseSetter gccs(gch, _gc_cause);
_result = gch->satisfy_failed_allocation(_word_size, _tlab);
assert(gch->is_in_reserved_or_null(_result), "result not in heap");
if (_result == NULL && GC_locker::is_active_and_needs_gc()) {
//如果GC后分配失败,则设置标识通知调用方GC后分配失败,需要再次GC
set_gc_locked();
}
}
HeapWord* GenCollectedHeap::satisfy_failed_allocation(size_t size, bool is_tlab) {
return collector_policy()->satisfy_failed_allocation(size, is_tlab);
}
GenCollectorPolicy::satisfy_failed_allocation的实现可以参考, 其主要流程如下:
补充说明如下:
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
public class CMSTest {
public static void main(String[] args) {
Set<Object> obj=new HashSet<>(10000);
int count=1;
for(int i=0;i<100000000;i++){
long start=new Date().getTime();
long[] a=new long[1000000];
long time=new Date().getTime()-start;
System.out.println("create long[] end,time->"+time+",count="+count+",now->"+new Date());
if(time>10){
//如果超过10ms,就意味着发生了young GC,之前创建的对象会promote到老年代
//此处clear则老年代GC时又会将这些promote到老年代的对象清理掉
obj.clear();
try {
Thread.sleep(25000);
count=0;
} catch (Exception e) {
}
}
obj.add(a);
count++;
}
}
}
启动脚本如下:
/home/openjdk/cppTest/bin/java -server -Xmx256M -Xms128M -XX:+UseConcMarkSweepGC -Xloggc:/home/openjdk/cppTest/gc.log -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCID -XX:+Verbose -XX:+TraceCMSState CMSTest
注意/home/openjdk/cppTest/bin/java指向的是我们自己编译源码后生成的java命令,debug版的,而不是正常的生产版本的。上述参数中Verbose是专门用于调试的,会打印更加信息的日志,TraceCMSState是跟踪CMS 老年代GC时的状态流转。通过sudo nohup sh cmsTest.h >./cms.log 2>&1 &命令可启动上述程序的运行,cms.log输出程序的控制台日志,gc.log打印gc的日志。
为了跟踪对象的分配,在分配内存的入口方法CollectedHeap::common_mem_allocate_noinit中加了如下日志:
同时也在年轻代的should_allocate方法和老年代的should_collect方法增加日志,如果不走TLAB走堆内存分配就会进入这两个方法,日志如下:
截取gc.log中一个完整的GC过程的日志说明如下:
OpenJDK 64-Bit Server VM (25.71-b00-debug) for linux-amd64 JRE (1.8.0-internal-root_2019_05_27_19_56-b00), built on Mar 8 2020 11:57:16 by "root" with gcc 4.4.7 20120313 (Red Hat 4.4.7-4)
Memory: 4k page, physical 4194304k(1280560k free), swap 0k(0k free)
CommandLine flags: -XX:InitialHeapSize=134217728 -XX:MaxHeapSize=268435456 -XX:MaxNewSize=89481216 -XX:MaxTenuringThreshold=6 -XX:NewSize=89481216 -XX:OldPLABSize=16 -XX:OldSize=178954240 -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCID -XX:+PrintGCTimeStamps -XX:+TraceCMSState -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+Verbose
Minimum heap 134217728 Initial heap 134217728 Maximum heap 268435456
1: Minimum gen0 89456640 Initial gen0 89456640 Maximum gen0 89456640
Minimum gen1 44761088 Initial gen1 44761088 Maximum gen1 178978816
//创建int数组
CollectedHeap::common_mem_allocate_noinit: klass->[I,size->2
//创建TLAB
DefNewGeneration::should_allocate->1,size->179079,tlab->1
//创建已加载类对应的class实例,用来保存该类的静态属性
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/lang/Class;,size->13
//创建Universe,SystemDictionary等组件中维护的一些常用的类实例
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/lang/OutOfMemoryError;,size->4
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/lang/OutOfMemoryError;,size->4
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/lang/OutOfMemoryError;,size->4
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/lang/OutOfMemoryError;,size->4
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/lang/OutOfMemoryError;,size->4
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/lang/OutOfMemoryError;,size->4
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/lang/Class;,size->14
CollectedHeap::common_mem_allocate_noinit: klass->[I,size->2
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/lang/NullPointerException;,size->4
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/lang/Class;,size->14
CollectedHeap::common_mem_allocate_noinit: klass->[I,size->2
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/lang/ArithmeticException;,size->4
CollectedHeap::common_mem_allocate_noinit: klass->[Ljava/lang/Object;,size->4
CollectedHeap::common_mem_allocate_noinit: klass->[Ljava/lang/Object;,size->11
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/lang/VirtualMachineError;,size->4
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/lang/VirtualMachineError;,size->4
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/lang/String;,size->3
CollectedHeap::common_mem_allocate_noinit: klass->[C,size->6
//创建源代码中字符串对应的String对象
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/lang/String;,size->3
CollectedHeap::common_mem_allocate_noinit: klass->[C,size->5
//创建代码及其API依赖的类实例
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/util/HashSet;,size->2
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/util/HashMap;,size->6
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/util/HashMap;,size->6
CollectedHeap::common_mem_allocate_noinit: klass->Ljava/util/WeakHashMap;,size->6
CollectedHeap::common_mem_allocate_noinit: klass->[Ljava/util/WeakHashMap$Entry;,size->10
//最后一个从年轻代成功分配的数组
CollectedHeap::common_mem_allocate_noinit: klass->[J,size->1000002
DefNewGeneration::should_allocate->1,size->1000002,tlab->0
//分配下一个时,年轻代内存不足了
CollectedHeap::common_mem_allocate_noinit: klass->[J,size->1000002
DefNewGeneration::should_allocate->1,size->1000002,tlab->0
TwoGenerationCollectorPolicy::mem_allocate_work: attempting locked slow path allocation
//first_only为true,表示只尝试在年轻代分配
TwoGenerationCollectorPolicy::mem_allocate_work: first_only->1
DefNewGeneration::should_allocate->1,size->1000002,tlab->0
//年轻代eden区分配失败,尝试从from区分配,但是属性should_allocate_from_space为false,不能从from区分配,所以返回NULL
DefNewGeneration::allocate_from_space(1000002): will_fail: false heap_lock: locked free: 8912896 should_allocate_from_space: NOT
returns NULL
//进入GenCollectedHeap::do_collection方法了,准备执行年轻代GC
2020-03-08T11:59:43.864+0800: 0.717: #0: [GC (Allocation Failure) Allocated 0 objects, 0 bytes concurrently
//判断是否需要执行年轻代GC
DefNewGeneration::should_allocate->1,size->1000002,tlab->0
//执行年轻代GC
2020-03-08T11:59:43.865+0800: 0.718: #0: [ParNewlevel=0 invoke=1 size=8000016
Expanding concurrent mark-sweep generation from 43712K by 7816K to 51528K
Expanded CMS gen for Par LAB
Expanding concurrent mark-sweep generation from 51528K by 7816K to 59344K
Expanded CMS gen for Par LAB
: 66865400->8677848(80543744), 0.1086520 secs]
//判断是否需要执行老年代GC,size为0,所以不需要
ConcurrentMarkSweepGeneration::should_collect->0,full->0,size->0,tlab->0
66865400->64682152(141312000)
//进入到老年代的gc_epilogue_work方法,这里只promote 了7个对象,因为这7个太大了,from区装不了,所以放到老年代了,后面的Contiguous available是老年代的未扩容空间
Promoted 7 objects, 56000112 bytes Contiguous available 118210560 bytes , 0.1097238 secs] [Times: user=0.14 sys=0.02, real=0.11 secs]
//GenCollectedHeap::do_collection结束,年轻代GC完成尝试分配,程序再次正常执行
DefNewGeneration::should_allocate->1,size->1000002,tlab->0
//因为老年代内存使用率超过初始设置值50%,触发老年代的后台GC
CMSCollector: collect for bootstrapping statistics: occupancy = 0.921605, boot occupancy = 0.500000
//执行InitialMarking
Thread 0x00007fe0e405a000 in CMS state 3
CMS Thread 0x00007fe0e405a000 continuing at CMS state 3
2020-03-08T11:59:43.989+0800: 0.842: #1: [GC (CMS Initial Mark) 2020-03-08T11:59:43.991+0800: 0.844: #1: [
checkpointRootsInitialWork, 0.0035132 secs]
[1 CMS-initial-mark: 56004304(60768256)] 74800512(141312000), 0.0052655 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
Thread 0x00007fe0e405a000 done - next CMS state 4
//执行Marking
Thread 0x00007fe0e405a000 in CMS state 4
CMS Thread 0x00007fe0e405a000 continuing at CMS state 4
2020-03-08T11:59:43.994+0800: 0.847: #1: [CMS-concurrent-mark-start]
2020-03-08T11:59:43.997+0800: 0.849: #1: [CMS-concurrent-mark: 0.002/0.002 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
Thread 0x00007fe0e405a000 done - next CMS state 5
//执行Precleaning
Thread 0x00007fe0e405a000 in CMS state 5
CMS Thread 0x00007fe0e405a000 continuing at CMS state 5
2020-03-08T11:59:43.997+0800: 0.850: #1: [CMS-concurrent-preclean-start]
(modUnionTable: 0 cards)2020-03-08T11:59:43.997+0800: 0.850: #1: [CMS-concurrent-preclean: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Thread 0x00007fe0e405a000 done - next CMS state 6
//执行AbortablePreclean
Thread 0x00007fe0e405a000 in CMS state 6
CMS Thread 0x00007fe0e405a000 continuing at CMS state 6
2020-03-08T11:59:43.997+0800: 0.850: #1: [CMS-concurrent-abortable-preclean-start]
//执行preclean_work时,会多次遍历modUnionTable,而外层的abortable_preclean会多次调用preclean_work,所以看到打印了多个modUnionTable: 0 cards
(modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards) (modUnionTable: 0 cards)
//因为到了最长的循环时间了,退出循环
CMS: abort preclean due to time 2020-03-08T11:59:49.153+0800: 6.006: #1: [CMS-concurrent-abortable-preclean: 2.053/5.155 secs] [Times: user=2.06 sys=0.00, real=5.15 secs]
Thread 0x00007fe0e405a000 done - next CMS state 7
//执行FinalMarking
Thread 0x00007fe0e405a000 in CMS state 7
CMS Thread 0x00007fe0e405a000 continuing at CMS state 7
2020-03-08T11:59:49.153+0800: 6.006: #1: [GC (CMS Final Remark) [YG occupancy: 18355 K (78656 K)]2020-03-08T11:59:49.153+0800: 6.006: #1: [checkpointRootsFinalWork2020-03-08T11:59:49.153+0800: 6.006: #1: [Rescan (parallel) , 0.0035317 secs]
//Reference实例处理
2020-03-08T11:59:49.157+0800: 6.009: #1: [refProcessingWork2020-03-08T11:59:49.157+0800: 6.009: #1: [weak refs processing, 0.0000320 secs]
//执行类卸载
2020-03-08T11:59:49.157+0800: 6.009: #1: [class unloading, 0.0020820 secs]2020-03-08T11:59:49.159+0800: 6.011: #1: [scrub symbol table, 0.0024840 secs]2020-03-08T11:59:49.161+0800: 6.014: #1: [scrub string table, 0.0016912 secs], 0.0063388 secs], 0.0116460 secs][1 CMS-remark: 56004304(60768256)] 74800512(141312000), 0.0117099 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
Thread 0x00007fe0e405a000 done - next CMS state 8
//执行Sweeping
Thread 0x00007fe0e405a000 in CMS state 8
CMS Thread 0x00007fe0e405a000 continuing at CMS state 8
2020-03-08T11:59:49.165+0800: 6.018: #1: [CMS-concurrent-sweep-start]
Collected 0 objects, 0 bytes
//当前老年代总共存活7个对象,释放了4个对象,这个7个就是最开始promote到老年代的七个
Live 7 objects, 56000112 bytes Already free 4 objects, 4768144 bytes
Total sweep: 60768256 bytes
Statistics for BinaryTreeDictionary:
------------------------------------
Total Free Space: 579110
Max Chunk Size: 579110
Number of Blocks: 1
Av. Block Size: 579110
Tree Height: 1
2020-03-08T11:59:49.166+0800: 6.019: #1: [CMS-concurrent-sweep: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
//执行Resizing
//计算元空间是否需要扩容
MetaspaceGC::compute_new_size:
minimum_free_percentage: 0.40 maximum_used_percentage: 0.60
used_after_gc : 4864.0KB
maximum_free_percentage: 0.70 minimum_used_percentage: 0.30
minimum_desired_capacity: 21296.0KB maximum_desired_capacity: 21296.0KB
//计算老年代是否需要扩容
From compute_new_size:
Free fraction 0.078395
Desired free fraction 0.400000
Maximum free fraction 0.700000
Capactiy 60768
Desired capacity 93340
Younger gen size 80543
unsafe_max_alloc_nogc 4632
contiguous available 118210
Expand by 32572250 (bytes)
//扩容老年代
Expanding concurrent mark-sweep generation from 59344K by 31812K to 91156K
Expanded CMS gen for Free ratio
Expanded free fraction 0.400021
Thread 0x00007fe0e405a000 done - next CMS state 1
//执行Resetting
Thread 0x00007fe0e405a000 in CMS state 1
CMS Thread 0x00007fe0e405a000 continuing at CMS state 1
2020-03-08T11:59:49.212+0800: 6.065: #1: [CMS-concurrent-reset-start]
2020-03-08T11:59:49.214+0800: 6.067: #1: [CMS-concurrent-reset: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Thread 0x00007fe0e405a000 done - next CMS state 2
//进入Idling,GC完成
CMS Thread 0x00007fe0e405a000 exiting collection CMS state 2