1. JVM G1 新生代分区大小及自动扩展
1.1. G1基于逻辑分代模型的设计
G1垃圾回收器,也是基于分代模型来实现。使用逻辑分区来实现G1的设计,region分为 新生代、老年代、大对象分区,还有自由分区。对象分配时,也是先进入新生代的eden区。
1.2. 内存大小分配方式
启动参数中通过 Xmx 最大值 Xms是最小值 参数,来设置堆内存大小。InitialHeapSize是堆内存的初始大小 默认0 ,MaxHeapSize 96MB。
1.2.1. 参数指定方式
第一种:指定堆内存新生代的大小,具体参数:MaxNewSize、NewSize;需要注意的是,如果只设置了Xmn这个参数,在G1里面,默认相当于设置了MaxNewSize = NewSize = Xmn ,相当于新生代内存的大小是固定的。
上述方式,定死了新生代内存的大小,在做ygc的时候,很有可能停顿预测模型没有办法生效。停顿预测模型+动态调整机制,是保障GC百分之九十能够维持在某个停顿时间内的关键。
第二种:指定新生代的占比,具体参数:NewRatio,用来设置老年代、新生代的比例的。例如,-XX:NewRatio=4,则代表老年代:新生代=4:1。
需要注意的是,如果只设置了NewRatio,对于新生代,相当于MaxNewSize = NewSize,新生代最大值最小值是相等的,即新生代空间大小 = heapSize / (NewRatio+1),如果设置了MaxNewSize、NewSize,同时又设置了NewRatio,此时NewRatio会被忽略。
注意:一般来说,在G1垃圾回收器里面,我们不推荐直接自己指定新生代的大小,并且指定成一个固定值。
1.2.2. G1启发式推断
第三种:没有指定新生代MaxNewSize最大值和NewSize最小值,或者只设置了其中一个,G1会根据G1MaxNewSizePercent(默认60%)和G1NewSizePercent(5%),来计算新生代内存大小。
如果没有设置新生代的大小,或者只设置了MaxNewSize 和 NewSize其中一个,此时新生代初始化的大小就是5%的堆内存空间,然后最大就是60%。
如果只设置了NewRatio,也无法达到自动计算新生代空间的效果。
一般,都是设定好堆内存的大小,新生代比例、新生代内存的大小让G1自动推断就好。
除非,系统运行了很长时间,发现一个非常合理的新生代的范围,那么可以考虑,把新生代的内存设置一下,一定是MaxNewSize 和 NewSize不相等,例如 100 10。
1.2.3. 老年代内存是多少?
老年代内存没有一个固定的大小,也没有具体的参数来设置,除非设置了NewRatio这个参数,会间接设置老年带的大小。
-XX:InitiatingHeapOccupancyPercent
这个参数,代表老年代的内存占用的时候会触发mixedGC混合回收。老年代内存使用的比例,默认最高是45%。
1.3. 我们应该怎么设置G1的新生代内存的大小?
必须要满足动态扩展机制,结合GC和停顿预测模型能够满足停顿时间的一个设置。
1.3.1. 如何满足G1新生代的动态扩展机制?
不要自己指定新生代的大小为一个固定值,不要直接指定Xmn,也不要直接只设置一个NewRatio、指定MaxNewSize = NewSize;如果一定要自己设置新生代的值,可以设置成范围,比如,MaxNewSize=100 NewSize=10,但是这个范围如果设置的不是很合理,很有可能还是会有性能问题。
1.3.2. 为什么要满足G1新生代的动态扩展?
为了满足用户设定的停顿时间(期望停顿时间),就需要做一个垃圾回收时间和程序运行时间的平衡。需要根据回收的时间,回收的内存空间的大小来做综合计算,来动态调整内存分区的占比,满足回收时间及停顿时间。
G1的新生代的动态扩展,可以做到动态调整YGC所需要的时间。
1.3.3. 新生代的动态扩展是怎么实现的?
每一种类型的分区有一个分区列表:新生代分区列表,老年代分区列表,大对象列表,自由分区列表。如果新生代需要扩展的时候,此时就从自由分区获取 region,加入到新生代分区列表中。
如果,自由分区没有了,无法给新生代提供分区,这个时候会找JVM去拓展新的分区,然后加入到新生代分区列表中,然后继续分配对象。
1.4. G1是怎么扩展新分区的?有什么规则限制?
200MB,500MB,如果说我直接扩展个400MB,能扩展吗?
1.4.1. 扩展新分区的规则是什么?
是根据-XX:GCTimeRatio这个参数去控制。这个参数表示,GC与应用的耗费时间比,G1中默认这个值是9,意思是,如果G1 GC时间与应用运行的时间占比不超过10%的时候,不需要动态扩展,如果GC时间占比超过了这个阈值,就需要做动态扩展。
1.4.2. 扩展的内存大小分区数量有什么限制?
有一个参数G1ExpandByPercentOfAvailable(默认是20),每次扩展的时候都从未使用的内存中申请20%的空间。并且,最小不能小于1MB,最大不能超过已经使用内存的一倍。
举例
堆内存一共最大64GB,使用了32GB,如果要做一次扩展,64-32(剩余内存)里面申请20%的空间出来。按照公式计算,20%乘以未分配的内存,小于1MB的(就给1MB -- 下限是1MB)。上限是当前已经分配的内存的一倍(已经使用的内存大小,当前例子的上限为 32G)。