JVM垃圾收集器

串行收集器

1、serial收集器(JDK1.7之前默认)
使用单线程处理所有垃圾回收工作,因为无需多线程交互,所以效率比较高,在进行垃圾收集时候,必须暂停其他所有的工作线程直到它收集结束。无法使用多处理器的优势,所以此收集器适合单处理器机器。当然,此收集器也可以用在小数据量(100M左右)情况下的多处理器机器上。
特点:CPU利用率最高,停顿时间即用户等待时间比较长。
使用场景:小型应用
JVM参数:可以使用-XX:+UseSerialGC打开。

并行收集器

1、parallel收集器(JDK8默认)
采用多线程来通过扫描并压缩堆,串行收集器在GC时会停止其他所有工作线程(stop-the-world),CPU利用率是最高的,所以适用于要求高吞吐量(throughput)的应用,但停顿时间(pause time)会比较长,所以对web应用来说就不适合,因为这意味着用户等待时间会加长。其实也就是多线程的serial收集器。
特点:停顿时间长,回收效率高,对吞吐量要求高。
使用场景:大型应用,科学计算,大规模数据采集等。
JVM参数:可以使用XX:+USeParNewGC打开。

并发收集器

1、CMS
CMS(Concurrent Mark Sweep)收集器是基于“标记-清除”算法实现的,它使用多线程的算法去扫描堆(标记)并对发现的未使用的对象进行回收(清除)。整个过程分为6个步骤,包括:
初始标记(CMS initial mark)
并发标记(CMS concurrent mark)
并发预清理(CMS-concurrent-preclean)
重新标记(CMS remark)
并发清除(CMS concurrent sweep)
并发重置(CMS-concurrent-reset)
其中初始标记、重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。其他动作都是并发的。
需要注意的是,CMS收集器无法处理浮动垃圾(Floating Garbage),可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。由于CMS并发清理阶段用户线程还在运行着,伴随程序的运行自然还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在本次收集中处理掉它们,只好留待下一次GC时再将其清理掉。这一部分垃圾就称为“浮动垃圾”。也是由于在垃圾收集阶段用户线程还需要运行,即还需要预留足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,需要预留一部分空间提供并发收集时的程序运作使用。在默认设置下,CMS收集器在老年代使用了68%的空间后就会被激活,这是一个偏保守的设置,如果在应用中老年代增长不是太快,可以适当调高参数-XX:CMSInitiatingOccupancyFraction的值来提高触发百分比,以便降低内存回收次数以获取更好的性能。要是CMS运行期间预留的内存无法满足程序需要,就会出现一次“Concurrent Mode Failure”失败,这时候虚拟机将启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。所以说参数-XX:CMSInitiatingOccupancyFraction设置得太高将会很容易导致大量“Concurrent Mode Failure”失败,性能反而降低。
还有一个缺点,CMS是一款基于“标记-清除”算法实现的收集器,这意味着收集结束时会产生大量空间碎片。空间碎片过多时,将会给大对象分配带来很大的麻烦,往往会出现老年代还有很大的空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。为了解决这个问题,CMS收集器提供了一个-XX:+UseCMSCompactAtFullCollection开关参数,用于在“享受”完Full GC服务之后额外免费附送一个碎片整理过程,内存整理的过程是无法并发的。空间碎片问题没有了,但停顿时间不得不变长了。虚拟机设计者们还提供了另外一个参数-XX: CMSFullGCsBeforeCompaction,这个参数用于设置在执行多少次不压缩的Full GC后,跟着来一次带压缩的。
该算法与并行收集器的另一个缺点是吞吐量的它使用更多的 CPU,为了使应用程序提供更好的体验,通过使用多个线程来执行扫描和收集。这种情况长时间的运行会使应用程序停顿下来,可以使用提高空间来换取高效的运行。但是,这种算法的使用不是默认的。您必须指定 XX: + USeParNewGC来使用它。如果你可以提供更多的CPU资源的话以避免应用程序暂停,那么你可以使用CMS收集器。假设你的堆的大小小于 4 Gb你必须分配大于 4 GB的资源。
特点:响应时间优先,减少垃圾收集停顿时间
使用场景:Web服务器/应用服务器、电信交换、集成开发环境。
JVM参数:可以使用-XX:+UseConcMarkSweepGC打开。

G1(JDK9、JDK10默认)

1、
G1垃圾收集器在JDK7 update 4之后对大于4G的堆有了更好的支持,G1是一个针对多处理器大容量内存的服务器端的垃圾收集器,其目标是在实现高吞吐量的同时,尽可能的满足垃圾收集暂停时间的要求。G1在执行一些Java堆空间中的全区域操作(如:全局标记)时是和应用程序线程并发进行的,因此减少了Java堆空间的中断比例。
它与前面的CMS收集器相比有两个显著的改进:一是G1收集器是基于“标记-整理”算法实现的收集器,也就是说它不会产生空间碎片,这对于长时间运行的应用系统来说非常重要。二是它可以非常精确地控制停顿,既能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,具备了一些实时Java(RTSJ)的垃圾收集器的特征。
首先将Java堆空间被划分为一个个相等的不连续的内存区域(regions),每个region都有一个分代的角色:eden、survivor、old(old还有一种细分 humongous,用来存放大小超过 region 50%以上的巨型对象),每个区域都是虚拟机中的一段连续内存空间。G1从多个region中复制存活的对象,然后集中放入一个region中,同时整理、清除内存(copying收集算法)。
2、G1内存的分配
1.TLAB(TLAB占用年轻代内存). 默认使用TLAB加速内存分配,。
2.Eden.如果TLAB不够用,则在Eden中分配内存生成对象。
3.Humongous.如果对象需要的内存超过一个region的50%以上,会忽略前两个步骤直接在老年代的humongous中分配(连续的Region)。
3、何时使用G1(-XX:+UseG1GC)
1.大内存中为了达到低gc延迟.
比如:heap size >=6G,gc pause <=0.5s
2.FullGC时间太长,或者太频繁。
调优参数:
-XX:MaxGCPauseMillis=200 用户设定的最大gc 停顿时间,默认是200ms.
-XX:InitiatingHeapOccupancyPercent=45 默认是45,也就是heap中45%的容量被使用,则会触发concurrent gc。
4、G1垃圾回收步骤详解
G1提供了两种GC模式,Young GC和Mixed GC,两种都是Stop The World(STW)的
5、G1 Young GC(STW)
1.当eden数据满了,则触发g1 YGC
2.并行的执行:
YGC 将 eden region 中存活的对象拷贝到survivor,或者直接晋升到Old Region中;将Survivor Regin中存活的对象拷贝到新的Survivor或者晋升old region。
3.计算下一次YGC eden、Survivor的尺寸
6、G1 Mix GC
在G1 GC中,它主要是为Mixed GC提供标记服务的,并不是一次GC过程的一个必须环节。global concurrent marking的执行过程分为五个步骤:
初始标记(initial mark,STW)
在此阶段,G1 GC 对根进行标记。该阶段与常规的 (STW) 年轻代垃圾回收密切相关。
根区域扫描(root region scan)
G1 GC 在初始标记的存活区扫描对老年代的引用,并标记被引用的对象。该阶段与应用程序(非 STW)同时运行,并且只有完成该阶段后,才能开始下一次 STW 年轻代垃圾回收。
并发标记(Concurrent Marking)
G1 GC 在整个堆中查找可访问的(存活的)对象。该阶段与应用程序同时运行,可以被 STW 年轻代垃圾回收中断
最终标记(Remark,STW)
该阶段是 STW 回收,帮助完成标记周期。G1 GC 清空 SATB 缓冲区,跟踪未被访问的存活对象,并执行引用处理。
清除垃圾(Cleanup,STW)
在这个最后阶段,G1 GC 执行统计和 RSet 净化的 STW 操作。在统计期间,G1 GC 会识别完全空闲的区域和可供进行混合垃圾回收的区域。清理阶段在将空白区域重置并返回到空闲列表时为部分并发
7、G1收集策略
在这里插入图片描述
但是老年代的处理流程不一样了,因为任何时候如果要想标注老年代的不用内存空间,都需要进行一些暂停,而G1之中的最大好处它不用进行全内存扫描,只需要按照区域来进行扫描即可。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

使用场景:
JVM参数:可以使用–XX:+UseG1GC打开。
JDK8对G1的优化:在java8 udpate 20中对G1收集器采用了字符串重复消除技术(String deduplication),之前字符串以及内部的char[]数组大量消耗了内存空间,在新的G1垃圾收集器中,将会对内存中重复的字符串进行优化,使他们指向同一个字符数组,以避免相同的字符串出现而使堆处理效率低下,你可以使用 -XX:+UseStringDeduplicationJVM参数来开启。
G1 GC常用参数
我在这里所罗列的参数的默认值都是基于JDK8u45,所以可能后续的JDK版本会有些值不一样,这个读者可以直接通过JDK的官方帮助文档获取最新默认值信息。
-XX:+UseG1GC:启用G1 GC。JDK7和JDK8要求必须显示申请启动G1 GC,JDK可能会设置G1 GC为默认GC选项,也有可能会退到早期的Parallel GC,这个也请关注吧,JDK9预计2017年正式发布;
-XX:G1NewSizePercent:初始年轻代占整个Java Heap的大小,默认值为5%;
-XX:G1MaxNewSizePercent:最大年轻代占整个Java Heap的大小,默认值为60%;
-XX:G1HeapRegionSize:设置每个Region的大小,单位MB,需要为1,2,4,8,16,32其一,默认是堆内存的1/2000。前面我们讲过大对象概念,如果这个值设置比较大,那么大对象就可以进入Region了,同样地,这样做的坏处是直接干预了各年龄代的分配大小;
-XX:ConcGCThreads:与Java应用一起执行的GC线程数量。默认是Java线程的1/4。减少这个参数的数值可能会提升并行回收的效率,即提高系统内部吞吐量(系统是一个整体,CPU资源大家都需要占用),不过如果这个数值过低,也会导致并行回收机制耗时加长;
-XX:+InitiatingHeapOccupancyPercent(简称IHOP):G1内部并行循环启动的设置值,默认为Java Heap的45%。这个可以理解为老年代空间占用的空间,GC收集后需要低于45%的占用率。这个值主要是为了决定在什么时间启动老年代的并行回收循环,这个循环从初始化并行回收开始,可以避免Full GC的发生;
-XX:G1HeapWastePercent:G1不会回收的内存大小,默认是堆大小的5%。GC会收集所有的Region,如果值达到5%,就会停下来不再收集了; -XX:G1MixedGCCountTarget:设置并行循环之后需要有多少个混合GC启动,默认值是8个。老年代Regions的回收时间通常比年轻代的收集时间要长一些,所以如果混合收集器比较多,可以允许G1延长老年代的收集时间;
-XX:+G1PrintRegionLivenessInfo:这个参数需要和-XX:+UnlockDiagnosticVMOptions配合启动,这可以理解,它们本身就是属于VM的调试信息。如果开启了,VM会打印堆内存里每个Region的存活对象信息。这个信息在标记循环结束后可以打印出来;
-XX:G1ReservePercent:这个值是为了保留一些空间用于年代之间的提升,默认值是堆空间的10%。注意这个空间保留后就不会用在年轻代了,大家可以看到GC日志里输出显示,我们大量执行的是年轻代回收,所以如果你的应用里面有比较大的堆内存空间、比较多的大对象存活,那还是减少一点保留空间吧,这样会给年轻代更多的预留空间、GC之间更长的处理时间;
-XX:+G1SummarizeRSetStats:这个也是一个VM的调试信息。如果启用,会在VM推出的时候打印出RSets的详细总结信息。如果启用-XX:G1SummaryRSetStatsPeriod参数,就会阶段性地打印RSets信息;
-XX:+G1TraceConcRefinement:这个也是一个VM的调试信息。如果启用,并行回收阶段的日志就会被详细打印出来;
-XX:+GCTimeRatio:大家知道,GC的有些阶段是需要Stop-the-World,即停止应用线程的,这个参数就是计算花在Java应用线程上和花在GC线程上的时间比率,默认是9。这个参数主要的目的是让用户可以控制花在应用上的时间,G1的计算公式是100/(1+GCTimeRatio),这样如果采用9,则最多10%的时间会花在GC工作上面。Parallel GC的默认值是99,表示1%的时间被用在GC上面,这是因为Parallel GC贯穿整个GC,而G1则根据Region来进行划分,不需要全局性扫描Java Heap;
-XX:+UseStringDeduplication:手动开启Java String对象的分割工作,这个是JDK8u20之后新增的参数,主要用于相同String避免重复申请内存,节约Region的使用;
-XX:MaxGCPauseMills:G1停止执行的一个目标值,单位是毫秒,默认是200毫秒,这个值不一定真的会达到。这个参数会通过控制年轻代的大小来实现目标。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值