适用场景:
面向服务端应用,针对具有大内存、多处理器的机器。
最主要的应用是需要低GC延迟,并具有大堆的程序
Hotspot垃圾收集器中,除了G1以外,其它垃圾收集器使用内置的JVM线程执行GC的多线程操作,而G1 GC可以采用应用线程承担后台运行的GC工作,即当JVM的GC线程处理速度慢时,系统调用应用程序线程帮助加速垃圾回收过程。
G1回收器垃圾回收过程:
G1 GC的垃圾回收过程主要包括三个环节:年轻代GC、老年代并发标记过程、混合回收、如果需要,单线程、独占式、高强度的Full GC还是继续存在的。它针对GC的评估失败提供了一种失败保护机制,即强力回收。
回收过程一:年轻代GC。JVM启动时,G1 先准备好Eden区,程序在运行过程中不断创建对象到Eden区,当年轻代的Eden区用尽时,G1会启动一次年轻代垃圾回收过程,G1的年轻代收集阶段是一个并行的独占式收集器。在年轻代回收期,年轻代垃圾回收只会回收Eden区和Survivor区,G1 GC暂停所有应用程序线程的执行,并创建回收集,回收集是指需要被回收的内存分段的集合,年轻代回收过程的回收集包含年轻代Eden区和Survivor区所有的内存分段。然后开始回收过程:
第一阶段,根扫描。根是指static变量指向的对象,正在执行的方法调用链条上的局部变量等。根引用连同RSet记录的外部引用作为扫描存活对象的入口。
第二阶段,更新RSet。处理dirty card queue中的card,更新RSet。此阶段完成后,RSet可以准确的反映老年代对所在的内存分段中对象的引用。
第三阶段,处理RSet。识别被老年代对象指向的Eden区中的对象,这些被指向的Eden中的对象被认为是存活的对象。
第四阶段,复制对象。此阶段,对象树被遍历,Eden区内存段中存活的对象会被复制到Survivor区中空的内存分段,Survivor区内存段中活的对象如果年龄未到阈值,年龄会加1,达到阈值会被复制到Old区中空的内存分段。如果Survivor空间不够,Eden空间的部分数据会直接晋升到老年代空间。
第五阶段,处理引用。处理Soft,Weak,Phantom,Final,JNI Weak等引用。最终Eden空间的数据为空,GC停止工作,而目标内存中的对象都是连续存储的,没有碎片,所以复制过程中可以达到内存整理的效果,减少碎片。
回收过程二:并发标记过程。
初始标记阶段(Root Region Scanning)。标记从根节点直接可达的对象。这个阶段是STW的,并且会触发一次年轻代GC。
根区域扫描。G1 GC扫描Survivor区直接可达的老年代区域对象,并标记被引用的对象。这一过程必须在Young GC之前完成。
并发标记(Concurrent Marking)。在整个堆中进行并发标记(和应用程序并发执行),此过程可能被young GC中断。在并发标记阶段,若发现区域对象中的所有对象都是垃圾,那这个区域会被立即回收。同时,并发标记过程中,会计算区域的对象活性(区域中存活对象的比例)。
再次标记(Remark)。由于应用程序持续进行,需要修正上一次的标记结果。是STW的。G1中采用了比CMS更快的初始快照算法SATB(snapshot-at-the-beginning)。
独占清理(cleanup,STW)。计算各个区域的存活对象和GC回收比例,并且进行排序,识别可以混合回收的区域。为下阶段做铺垫。是STW的。这个阶段并不会实际上去做垃圾的收集。
并发清理阶段。识别并清理完全空闲的区域。
回收过程三:
当越来越多的对象晋升到老年代Old Region时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即Mixed GC,该算法并不是一个Old GC,除了回收整个Young Region,还会回收一部分的Old Region。需要注意的是:一部分老年代,而不是全部老年代。可以选择哪些Old Region进行收集,从而可以对垃圾回收的耗时时间进行控制。也需要注意的是Mixed GC并不是Full GC。
并发标记结束后,老年代中百分百为垃圾的内存分段被回收了,部分为垃圾的内存分段被计算出来。默认情况下,这些老年代的内存分段会分8次(可以通过-XX:G1MixedGCCountTarget设置)被回收。
混合回收的回收集(Collection Set)包括八分之一的老年代内存分段,Eden区内存分段,Survivor区内存分段。混合回收的算法和年轻代回收的算法完全一样,只是回收集多了老年代的内存分段。
由于老年代中的内存分段默认分8次回收,G1会优先回收垃圾多的分段。垃圾占内存分段比例越高的,越先回收。并且有一个阈值会决定内存分段是否被回收,-XX:G1MixedGCLiveThresholdPercent,默认65%,就是垃圾占内存分段比例要达到65%才会被回收。如果垃圾占比太低,意味着存活的对象占比高,在复制的时候会花费更多的时间。
混合回收并不一定要进行8次。有一个阈值-XX:G1HeapWastePercent,默认值为10%,就是允许整个堆内存中有10%的空间被浪费,也就是如果发现可以回收的垃圾占堆内存的比例低于10%,则不再进行混合回收。因为GC会花费很多时间,但是会收到的内存却很少。
举例:一个Web服务器,Java进程最大堆内存为4G,每分钟响应1500个请求,每45秒会新分配大约2G内存。G1会每45s进行一次年轻代回收,每31小时整个堆的使用率会达到45%,会开始老年代并发标记过程,标记完成后,开始四到五次的混合回收。
回收过程四:Full GC
G1的初衷就是要避免Full GC的出现,但是如果上述工作方式不能正常工作,G1会停止应用程序的执行(STW),使用单线程的内存回收算法进行垃圾回收,性能会非常差,应用程序停顿时间会很长。
要避免Full GC的发生,一旦发生需要进行调整。当堆内存太小,就容易发生Full GC,当G1在复制存活对象时,没有空闲的内存分段可用,则会回退到Full GC,这种情况下,可以增大内存来解决。
导致G1 Full GC的原因可能有两个:
1.Evacuation的时候没有足够的to-space来存放晋升的对象。
2.并发处理过程完成之前空间耗尽。
还没有评论,快来发表第一个评论!