18_G1 分代回收原理
约 1066 字大约 4 分钟
2024-08-10
G1 对应内存大小设置
我们知道了 G1 对应的是一大堆的 Region 内存区域,每个 Region 大小是一致的
那到底有多少个 Region 呢?每个 Region 的大小是多少呢?
我们用 -Xms 和 -Xmx 设置堆内存大小,用 -XX:+UseG1GC 参数指定垃圾回收器,JVM 启动的时候看到使用的是 G1 垃圾回收器,此时会自动用堆大小除以 2048
因为 JVM 最多可以有 2048 个 Region,每个 Region 的大小必须是 2 的倍数,比如堆大小是 4G,4096MB 除以 2048 个 Region,每个 Region 的大小就是 2MB,如下图
大概就这样子来决定 Region 的数量和大小的,保持默认的计算方式就可以了,当然,也可以通过 -XX:G1HeapRegionSize 指定 Region 大小

一开始,新生代对堆内存的占比默认是 5%,也就是占据 200MB 左右的内存,对应 100 个 Region,可以通过 -XX:G1NewSizePercent 来设置新生代初始占比
在系统运行中,JVM 会不停的给新生代增加更多的 Region,但占比不会超过 60%,可以通过 -XX:G1MaxNewSizePercent
一旦 Region 如果进行了垃圾回收,新生代的 Region 数量还会减少,这些都是动态的
新生代还有 Eden 和 Survivor 的概念吗
虽然 G1 中把内存划分为了很多的 Region,但是还是有新生代、老生代的区分
属于新生代的 Region 中,还是可以区分哪些属于 Eden,哪些属于 Survivor
新生代的参数 -XX:SurvivorRatio=8,比如说新生代初始的时候有 100 个 Region,那么 80 个 Region 就是 Eden,两个 Survivor 各占 10 个 Region
随着对象不停的在新生代里分配,属于新生代的 Region 会不断增加,Eden 和 Survivor 对应的 Region 也会不断增加
G1 新生代垃圾回收
既然 G1 的新生代也有 Eden 和 Survivor 的区分,那么触发垃圾回收机制也都是类似的
随着不停的在新生代 Eden 对应的 Region 中放对象,JVM 就会不停的给新生代加入更多的 Region,直到新生代占堆大小的最大比例 60%,假设 1000 个 Region,Eden 可能占 800 个 Region,每个 Survivor 100 个 Region

Eden 区占满了对象时触发新生代的 GC,G1 就会用之前说过的复制算法来进行垃圾回收,进入 Stop the World 状态
将 Eden 对应 Region 中的存活对象放入 S1 的 Region 中,接着回收掉 Eden 对应的 Region 中的垃圾对象
这个过程和之前有区别,G1 可以设置目标 GC 最多可以让系统停顿多长时间,通过 -XX:MaxGCPauseMils 参数设置,默认值是 200ms
对象什么时候进入老年代
按照默认新生代最多只能占据堆内存 60% 的 Region 来推算,老年代最多可以占 40% 的 Region
对象进入老年代的条件和之前几乎是一样的:
1、对象在新生代躲过了很多次垃圾回收,达到了一定年龄进入老年代,参数
-XX:MaxTenuringThreshold参数设置2、动态年龄判定规则,某次新生代
GC后,存活对象超过了Survivor的50%
大对象 Region
以前说的大对象可以直接进入老年代,那 G1 这套内存模型下呢?
G1 提供了专门的 Region 来存放大对象,而不是让大对象进入老年代的 Region 中
在 G1 中,大对象的判定规则是一个大对象超过了一个 Region 大小的 50%,就会被放入大对象专门的 Region 中
如果一个对象太大,可能会横跨多个 Region 来存放

在 G1 中,新生代和老年代的 Region 是不断变化的,很多空着的 Region 可以用来存放大对象
在新生代、老年代垃圾回收的时候,会顺带着大对象 Region 一起回收