G1回收器
在使用ParNew + CMS进行垃圾回收时,会导致长时间的
STW
,而STW的本质就是停止系统的工作线程,让系统无法对外服务,同时让GC线程进行垃圾回收的。如果回收时间较长,那么会给系统性能带来较大的影响
因此G1回收器出现了,它的特点有
- 将Java堆分为多个大小相等的
Region
- 每个
都有可能属于新生代或老年代Region
- 可以设置
,比如在一小时内最多停顿一分钟垃圾回收的预期停顿时间
G1如何做到设置 垃圾回收的预停顿时间
垃圾回收的预停顿时间
G1会追踪每个
Region
中的
回收价值(当前Region中有多少垃圾对象,需要耗费多长时间)
,通过追踪,G1可以比较出本次回收哪些
Region
的性价比最高(时间最短,回收内存最多),在有限的时间内尽可能回收掉更多的垃圾对象
如何启用G1回收器
可以通过JVM参数
-XX:UseG1GC
来指定垃圾回收器为G1
如何设定G1的内存大小
在指定了G1回收器后,它会根据
-Xmx/2048
来计算每个Region的内存大小,因为JVM中最多支持2048个Region,并且每个Region的内存必须是2的倍数
比如你设定的
-Xmx4096M
,那么按照G1的规则来,4096 / 2048 = 2MB,那么每个Region的内存大小就为2MB
当然,你也可以手动通过JVM参数
-XX:G1HeapRegionSize
来指定每个Region的大小
系统初始化状态,新生代的内存占比为5%,大概为100个Region,可以通过JVM参数
-XX:G1NewSizePercent
来设置这个比例,但是一般都推荐保持这个设置,因为G1会动态的调整新生代、老年代的内存占比,但是默认情况下新生代的最大占比不能超过
60%
,可以通过JVM参数
-XX:G1MaxNewSizePercent
来设置这个比例
G1中新生代还保留着Eden、Survivor的划分
虽然G1回收器将内存分为了多个Region,但是新生代依旧保留着原本的区域划分,即Eden、Survivor1、Survivor2,并且默认情况下它们的比例为
8:1:1
,可以通过JVM参数
-XX:SurvivorRatio
来设置比例,比如系统初始化时,新生代大约有100个Region,那么其中大约有80个Region是Eden区的,10个Region是S1区的,10个Region是S2区的
G1中新生代的垃圾回收算法
G1中新生代在Eden区满时会触发GC,垃圾回收算法还是使用
复制算法
,同时进入
STW
,但是因为G1设置了预期停顿时间,可以让系统在GC时指定最多停顿的时间,而不像ParNew回收器那样,极端情况下能够一直停顿,而对系统性能造成严重影响
可以通过JVM参数
-XX:MaxGCPauseMills
来设置这个时间
G1中对象什么时候进入老年代
和原来一样,进入老年代的条件有,
-
,默认为15年,通过JVM参数超过指定年龄的对象
指定这个阈值-XX:MaxTenuringThreshold
-
,当某一年龄动态年龄判断
的对象占有了新生代内存的以下(包括)
以上,那么所有超过该年龄的对象都会进入老年代50%
- 新生代GC后,存活对象内存大小超过Survivor区内存大小,这些对象会进入老年代
G1中如何处理大对象
在G1中,大对象不会进入老年代,G1会提供专门的Region来存放这些大对象
G1中大对象的判定规则为:该对象超过了一个Region大小的50%,那么这个对象就是大对象
大对象可能会横跨多个Region
G1中大对象的回收
会在新生代、老年代发生GC时顺带回收掉
什么时候触发新生代、老年代混合垃圾回收
JVM参数
-XX:InitiatingHeapOccupancyPercent
,默认为45,即当老年代占堆内存比例达到45%时,即Region数量大约为1000个时,会尝试触发一个混合回收
Mixed GC
G1回收器的回收过程
- 初始标记
- 并发标记
- 最终标记
- 混合回收
初始标记
和CMS一样,这个步骤会触发
STW
,此时会标记那些被
GC Roots
直接引用的对象,比如静态变量,局部变量等
并发标记
这个步骤,工作线程和GC线程会同时工作,此时会追踪所有存活的对象,同时JVM会对这个步骤中对象的一些修改记录起来,比如哪个对象被新建了,哪个对象失去了引用等
最终标记
这个步骤会触发
STW
,对并发标记阶段记录的被修改的对象进行最终标记,找出存活对象和垃圾对象
混合回收
这个步骤会计算老年代中每个Region中存活的对象数量,存活对象的占比和执行垃圾回收的预期性能和效率,然后停止工作线程,对部分Region进行回收,因为G1需要控制垃圾回收时的停顿时间(用户指定)
混合回收的区域包括新生代、老生代和大对象Region
这个阶段会执行多次,JVM参数
-XX:G1MixedGCCountTarget
指定了混合回收执行的次数,默认为8次,每执行完一次会启动工作线程对外提供服务,一段时间后继续进行
STW
,执行混合回收,这样可以尽可能的让系统停顿的时间变短,尽量减少对象系统性能的影响
JVM参数
-XX:G1HeapWastePercent
,默认为5,当G1中Region空闲比例达到5%时,混合回收就结束了
在混合回收时,对Region的回收都是基于
复制算法
的,即把Region中的存活对象复制到其他Region中,然后清理掉当前Region中的垃圾对象,那么G1回收器进行垃圾回收时,是不会产生内存碎片的
JVM参数
-XX:G1MixedGCLiveThresholdPercent
,默认为85,G1回收器在确定回收Region时,必须要保证该Region中的存活对象在85%以下,否则使用复制算法的回收效率是非常低的
当G1回收失败时,如在回收时拷贝对象的时候,回收器发现没有空闲的Region可以放入存活对象了,那么就会触发一次回收失败,失败之后,JVM就会切换到单线程回收器,进行标记-清理,压缩和整理,空闲出一批Region,这个过程是非常慢的,又因为它是需要
STW
的,所以对系统的性能影响非常大