从年轻代和老年代的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。 增补阐明如下:
- 将Reference实例链表中剩余的Reference实例参加pending列表只实用于堆内存压缩式GC,正常的前台GC和背景GC都是在FinalMarking环节完成了该利用。
- 在各Genaration实验gc_epilogue时都会整理ChunkPool,在do_collection中还实验了一遍,主要是开释do_collection方法本身占用的ChunkPool内存。
- 通过老年代collect方法触发的堆内存压缩和前台GC都不会实验compute_new_size方法,该方法就是背景GC的Resizing步调的焦点,会根据当前占用的内存和设置的内存最大空闲率,内存最小空闲率来做须要的扩容和缩容。
- 如果start_level为0时,年轻代GC完成后会将size置为0,当进入老年代的should_collect方法时,由于size为0,该方法返回false,就不会触发老年代的GC。什么情况下start_level为0了?full为false时大概full为true,max_level为0时。
- 联合上面start_level的分析,可知max_level为1的时候,full为false时do_collection只调用年轻代GC,full为true时实际只调用老年代GC,当max_level为0的时候只调用年轻代的collect方法,固然代码中是循环调用start_level到max_level之间的Genaration的collect方法,但是实际只调用一次。
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(), "recondition#1"); //校验获取了Heap_lock assert(Heap_lock->is_locked(), "recondition#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方法的主流程如下:
增补阐明如下:
- 上述步调中每次实验分配乐成后就会立刻返回,分配失败则继续走下一步。第一步实验直接分配而不是触发GC是由于一旦元空间不敷了,大概会触发多个线程实验VM_CollectForMetadataAllocation,但是VMThread一次只处理一个,即存在其他线程已经触发GC开释了富足的内存,以是这里再做一次实验分配。
- 第二步判断时,只有CMS算法和G1算法在GC时会卸载Klass,这两种算法在默认设置下返回true,其他的返回false。以CMS的实现为例,在调用该方法判断时,会将MetaspaceGC的_should_concurrent_collect属性置为false,CMSCollector::shouldConcurrentCollect()方法返回true,CMS Thread开始实验老年代的背景GC并将_should_concurrent_collect置为true,即此时CMS Thread与VMThread在并行实验,CMS Thread大概已经开释了一部分内存,以是该判断返回true以后,会多实验一次元空间扩容再分配。
- 此场景下触发的老年代GC实验完后,元空间也会实验compute_new_size,即会根据GC后的元空间内存占用量做须要的扩容大概缩容,一样平常不会是最大容量,以是在GC完成后直接实验内存分配失败会再实验扩容元空间并分配。
- 扫除软引用本身开释的内存并不多,以是第一次时并不扫除,而是在第一次不清晰的情况下依然分配失败了才会思量扫除软引用,这里的扫除是指全部扫除,纵然是刚创建不久的,默认情况下一段时间内不再访问的软引用本身会被主动扫除,这个时间在默认设置下是由根据上一次GC后的堆内存盘算出来的最大空间内存盘算出来的。
- satisfy_failed_metadata_allocation方法是在一个while(true)循环里实验VM_CollectForMetadataAllocation的,即会不绝的GC直到内存分配乐成,上述流程的末了一步实际就是关照satisfy_failed_metadata_allocation方法GC后内存依然不敷,然后重新触发下一轮的VM_CollectForMetadataAllocation调用。
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->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;}增补阐明如下:
- 上述流程中,每一次实验分配,如果分配乐成则会直接返回,如果都分配失败则会开始下一次循环
- 如果有线程处于JNI关键区且背景GC被触发了,此时如果待分配的内存用于TLAB,则直接返回NULL,调用方会重新调用此方法实验分配;此时如果当火线程就处于JNI关键区中则直接返回NULL,从而让该线程从关键区中退出,如果不在JNI关键区中则壅闭直到全部线程从JNI关键区中退出,末了一个退出的线程会触发GC。
- 上述for循环是有次数限定的的,一旦full GC即老年代的GC次数超过AdaptiveSizePolicyGCTimeLimitThreshold,默认值是5,JVM会以为体系内存不敷然后抛出非常终止Java历程,full GC实验完成后会通过check_gc_overhead_limit方法查抄是否超过限定,如果是则将_gc_overhead_limit_exceeded置为true,参考set_gc_overhead_limit_exceeded方法的调用链,如下:
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的实现可以参考, 其主要流程如下:
增补阐明如下:
- 正常情况下进入satisfy_failed_allocation前会查抄是否有线程处于JNI关键区,如果有则壅闭直到末了全部线程从JNI关键区中退出,但是并发下不扫除从JNI关键区退出后到VMThread开始实验VM_CollectForMetadataAllocation时又有新的线程进入到JNI关键区中,以是在开始处做了一道查抄,此时如果有未扩展的空间则实验扩展并分配,如果分配失败直接返回NULL
- 上述流程中实验分配都是先实验在年轻代分配,如果分配失败,再实验在老年代分配
- 正常情况下内存分配失败会触发年轻代的GC,如果老年代内存富足,年轻代GC顺遂完成,则下一步直接分配内存乐成顺遂返回;如果老年代内存不敷,年轻代GC实验由于promote会失败而终止,这时下一步直接分配内存,年轻代肯定内存不敷,通常就在老年代分配了,这种情形下固然分配乐成了,但是年轻代并没有GC,等下一次再进入该方法时,就会触发老年代的GC但是不扫除软引用,老年代GC完成,正常这种情形下也是会在老年代分配,注意此时年轻代GC还未实验;再下一次进入该方法时就会触发年轻代的GC,由于上一次的老年代GC已经回收了富足的空间,可以正常实验年轻代GC了,然后正常从年轻代分配内存。上述分析中如果在老年代分配内存失败了,则会触发整理软引用的老年代GC,GC完成后实验分配内存,如果还是失败则会循环多实验频频,直到超过full GC的次数限定。
7、 Debug日志分析
可以通过在源码中增长调试日志然后重新编译的的方式来进一步深入相识步调的运行逻辑,测试用比方下:
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 2684354561: Minimum gen0 89456640 Initial gen0 89456640 Maximum gen0 89456640Minimum gen1 44761088 Initial gen1 44761088 Maximum gen1 178978816//创建int数组CollectedHeap::common_mem_allocate_noinit: klass->[I,size->2//创建TLABDefNewGeneration::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->1000002DefNewGeneration::should_allocate->1,size->1000002,tlab->0//分配下一个时,年轻代内存不敷了CollectedHeap::common_mem_allocate_noinit: klass->[J,size->1000002DefNewGeneration::should_allocate->1,size->1000002,tlab->0TwoGenerationCollectorPolicy::mem_allocate_work: attempting locked slow path allocation//first_only为true,表现只实验在年轻代分配TwoGenerationCollectorPolicy::mem_allocate_work: first_only->1DefNewGeneration::should_allocate->1,size->1000002,tlab->0//年轻代eden区分配失败,实验从from区分配,但是属性should_allocate_from_space为false,不能从from区分配,以是返回NULLDefNewGeneration::allocate_from_space(1000002): will_fail: false heap_lock: locked free: 8912896 should_allocate_from_space: NOT returns NULL//进入GenCollectedHeap::do_collection方法了,准备实验年轻代GC2020-03-08T11:59:43.864+0800: 0.717: #0: [GC (Allocation Failure) Allocated 0 objects, 0 bytes concurrently//判断是否需要实验年轻代GCDefNewGeneration::should_allocate->1,size->1000002,tlab->0//实验年轻代GC2020-03-08T11:59:43.865+0800: 0.718: #0: [ParNewlevel=0 invoke=1 size=8000016Expanding concurrent mark-sweep generation from 43712K by 7816K to 51528KExpanded CMS gen for Par LABExpanding concurrent mark-sweep generation from 51528K by 7816K to 59344KExpanded 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%,触发老年代的背景GCCMSCollector: collect for bootstrapping statistics: occupancy = 0.921605, boot occupancy = 0.500000//实验InitialMarkingThread 0x00007fe0e405a000 in CMS state 3CMS Thread 0x00007fe0e405a000 continuing at CMS state 32020-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 //实验MarkingThread 0x00007fe0e405a000 in CMS state 4CMS Thread 0x00007fe0e405a000 continuing at CMS state 42020-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 //实验PrecleaningThread 0x00007fe0e405a000 in CMS state 5CMS Thread 0x00007fe0e405a000 continuing at CMS state 52020-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 //实验AbortablePrecleanThread 0x00007fe0e405a000 in CMS state 6CMS Thread 0x00007fe0e405a000 continuing at CMS state 62020-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 7CMS Thread 0x00007fe0e405a000 continuing at CMS state 72020-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 8CMS Thread 0x00007fe0e405a000 continuing at CMS state 82020-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 bytesTotal sweep: 60768256 bytesStatistics for BinaryTreeDictionary:------------------------------------Total Free Space: 579110Max Chunk Size: 579110Number of Blocks: 1Av. Block Size: 579110Tree Height: 12020-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 91156KExpanded CMS gen for Free ratio Expanded free fraction 0.400021 Thread 0x00007fe0e405a000 done - next CMS state 1//实验Resetting Thread 0x00007fe0e405a000 in CMS state 1CMS Thread 0x00007fe0e405a000 continuing at CMS state 12020-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 |