G1收集器从JDK7开始引入,是面向服务端应用的一款垃圾回收器,它不再像传统收集器那样将堆内存固定划分为新生代、老年代,而是将整个堆拆分成多个大小相等的独立区域,这些区域就是Region。每个Region的大小是固定的,范围在1MB到32MB之间,具体大小由JVM根据堆的初始大小和最大大小自动计算,也可以通过参数-XX:G1HeapRegionSize手动指定。

Region的基本特性
每个Region在某一时刻只会扮演一种角色,常见的角色有四种:
- Eden Region:新生代的Eden区,用于存放新创建的对象
- Survivor Region:新生代的Survivor区,存放Minor GC后存活的对象
- Old Region:老年代区域,存放经过多次回收后仍然存活的对象
- Humongous Region:大对象区域,用于存储大小超过Region容量50%的大对象,这类对象会直接分配在老年代,并且如果对象过大,会连续占用多个Humongous Region
Region的角色不是固定不变的,在一次垃圾回收之后,原来的Eden Region和Survivor Region可能会被清空,之后可以重新分配为其他角色,这种动态角色切换是G1灵活管理内存的基础。
Region划分的优势
相比传统的固定分代布局,Region划分带来了多方面的优势:
- 避免了内存空间的浪费,传统分代中如果新生代对象全部存活,老年代没有足够空间的话会触发Full GC,而G1可以动态调整不同角色Region的数量,充分利用堆内存
- 支持增量回收,G1每次回收不需要回收整个堆,只需要选择一部分收益最高的Region进行回收,也就是Mixed GC,这样可以控制单次回收的停顿时间
- 大对象处理更灵活,Humongous Region的设计避免了大对象直接进入老年代导致的空间占用问题,也减少了大对象回收时的开销
基于Region的回收流程
G1的回收主要分为两种类型,都基于Region机制实现:
1. Young GC
当Eden Region的空间被占满时,就会触发Young GC,回收所有Eden Region和Survivor Region中的存活对象。存活的对象会被复制到新的Survivor Region或者晋升到Old Region,然后清空原来的Eden和Survivor Region,这些被清空的Region可以重新分配为其他角色。
下面是一段模拟Young GC后Region角色变化的伪代码:
// 假设初始有4个Eden Region,2个Survivor Region
List<Region> edenRegions = Arrays.asList(new Region("Eden"), new Region("Eden"), new Region("Eden"), new Region("Eden"));
List<Region> survivorRegions = Arrays.asList(new Region("Survivor"), new Region("Survivor"));
// Young GC触发,回收Eden和Survivor中的存活对象
void youngGC() {
// 存活对象复制到新的Survivor Region
List<Region> newSurvivorRegions = copyLiveObjects(edenRegions, survivorRegions);
// 年龄达到阈值的对象晋升到Old Region
List<Region> newOldRegions = promoteAgedObjects(survivorRegions);
// 清空原来的Eden和Survivor Region,重置为可用状态
resetRegions(edenRegions);
resetRegions(survivorRegions);
// 可用Region重新分配角色
reassignRegions(edenRegions, newSurvivorRegions, newOldRegions);
}
2. Mixed GC
当老年代占堆内存的比例达到阈值(-XX:InitiatingHeapOccupancyPercent,默认45%)时,就会触发Mixed GC。Mixed GC不仅会回收新生代的Region,还会选择一部分收益最高的Old Region进行回收,这里的收益指的是回收该Region能释放的空间大小以及回收所需的时间。
G1通过Remembered Set来记录每个Region之间的引用关系,避免全堆扫描。每个Region都有自己的Remembered Set,记录了其他Region中引用当前Region内对象的引用信息,这样在回收某个Region时,只需要扫描它的Remembered Set就可以知道哪些对象是被外部引用的,不需要遍历整个堆。
Region相关的常用JVM参数
以下是调整Region相关配置的常见参数:
| 参数名 | 作用 | 默认值 |
|---|---|---|
| -XX:G1HeapRegionSize | 设置每个Region的大小,必须是2的幂次,范围1MB-32MB | 根据堆大小自动计算 |
| -XX:MaxGCPauseMillis | 设置最大GC停顿时间目标,G1会根据这个目标选择回收的Region数量 | 200ms |
| -XX:InitiatingHeapOccupancyPercent | 触发Mixed GC的老年代占比阈值 | 45 |
| -XX:G1ReservePercent | 设置保留的空闲Region比例,防止对象晋升时内存不足 | 10 |
理解Region的划分机制是掌握G1收集器的核心,这种灵活的内存管理方式让G1在吞吐量和停顿时间之间取得了很好的平衡,适合对响应时间有要求的大内存应用。