JVM虚拟机——G1垃圾收集器分区Region

本文介绍了G1垃圾回收器的基本概念,包括其如何划分Java堆空间为多个大小相等的独立区域(Region)。探讨了不同类型的堆分区及其管理策略,并详细解释了Region大小的计算方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

G1之前的垃圾收集器都会将堆分成三个部分,新生代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation),根据不同的分区类型,采用不同的策略进行回收。

G1仍然保留新生代和老年代的概念,但新生代和老年代不再是固定的了,它们都是一系列区域(不需要连续)的动态集合。G1把连续的Java堆划分为多个大小相等的独立区域(Region),每一个Region都可以作为新生代的Eden空间、Survivor空间,或者老年代空间。G1堆和操作系统交互的最小管理单位称为分区(Heap Region,HR)或称堆分区。

G1的分区类型(HeapRegionType)大致可以分为四类:

  • 自由分区(Free Heap Region,FHR)

  • 新生代分区(Young Heap Region,YHR)

  • 大对象分区(Humongous Heap Region,HHR)

  • 老生代分区(Old Heap Region,OHR)

其中新生代分区又可以分为Eden和Survivor;大对象分区又可以分为:大对象头分区和大对象连续分区。

Humongous Heap Region专门用来存储大对象,只要大小超过了一个Region容量一半的对象即可判定为大对象。Region的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围为1MB~32MB,且应为2的N次幂,默认值为0。

超过了整个Region容量的超级大对象,将会被存放在N个连续的Humongous Region之中,G1的大多数行为都把Humongous Region作为老年代的一部分来进行看待。

在G1中每个分区的大小都是相同的,HR的大小直接影响分配和垃圾回收效率。如果过大,一个HR可以存放多个对象,分配效率高,但是回收的时候花费时间过长;如果太小则导致分配效率低下。为了达到分配效率和清理效率的平衡,HR有一个上限值和下限值,目前上限是32MB,下限是1MB(HR的大小只能为1MB、2MB、4MB、8MB、16MB和32MB),默认情况下,整个堆空间分为2048个HR(该值可以自动根据最小的堆分区大小计算得出)。

HR大小可由以下方式确定:

1、可以通过参数G1HeapRegionSize来指定大小,这个参数的默认值为0。

[root@jeespring ~]# java -XX:+PrintFlagsFinal -version | grep G1HeapRegionSize
   size_t G1HeapRegionSize          = 0             {product} {default}
java version "11.0.8" 2020-07-14 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.8+10-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.8+10-LTS, mixed mode)

2、启发式推断,即在不指定HR大小的时候,由G1启发式地推断HR大小。

HR启发式推断根据堆空间的最大值和最小值以及HR个数进行推断,设置InitialHeapSize(默认为0)等价于设置Xms,设置MaxHeapSize(默认为96MB)等价于设置Xmx。

[root@jeespring ~]# java -XX:+PrintCommandLineFlags -version
-XX:G1ConcRefinementThreads=4 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=132500800 
-XX:MaxHeapSize=2120012800 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 
-XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops 
-XX:+UseG1GC -XX:-UseLargePagesIndividualAllocation
java version "11.0.8" 2020-07-14 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.8+10-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.8+10-LTS, mixed mode)

堆分区默认大小的计算方式在HeapRegion.cpp中的setup_heap_region_size(),代码如下所示:

默认region_size 为 1M,最大为32M,HR个数为2048个

openjdk-jdk11u/src/hotspot/share/gc/g1/heapRegionBounds.hpp

openjdk-jdk11u/src/hotspot/share/gc/g1/HeapRegion.cpp

按照默认值计算,G1可以管理的最大内存为2048×32MB=64GB。假设设置xms=32G,xmx=128G,则每个堆分区的大小为32M,分区个数动态变化范围从1024到4096个。

region_size计算过程

  size_t region_size = G1HeapRegionSize;
  if (FLAG_IS_DEFAULT(G1HeapRegionSize)) {
    size_t average_heap_size = (initial_heap_size + max_heap_size) / 2;
    region_size = MAX2(average_heap_size / HeapRegionBounds::target_number(),
                       HeapRegionBounds::min_size());
  }
  int region_size_log = log2_long((jlong) region_size);
  region_size = ((size_t)1 << region_size_log);
  if (region_size < HeapRegionBounds::min_size()) {
    region_size = HeapRegionBounds::min_size();
  } else if (region_size > HeapRegionBounds::max_size()) {
    region_size = HeapRegionBounds::max_size();
  }

1、没有配置G1HeapRegionSize

我们线上配置的JVM参数-Xmx4096m 和 -Xms4096m,根据配置计算过程如下:

average_heap_size=(4096m+4096m)/2=4096m

region_size=MAX2(4096m/2048, 1m)=2m

region_size_log=21(log2_long函数是获取以2为底,region_size的对数,2^21=2*1024*1024<=2m)

region_size=2^21=2m(region_size_log左移1位,也就是2^21)

region_size=2m(MIN_REGION_SIZE(1m)<= 2m <=MAX_REGION_SIZE(32m))

2、配置G1HeapRegionSize大小

JVM参数配置:-Xmx4096m 、-Xms4096m 、-XX:G1HeapRegionSize=8m,由于配置了G1HeapRegionSize的大小,FLAG_IS_DEFAULT(G1HeapRegionSize)条件为false,计算过程如下:

region_size=8m

region_size_log=22(2^23=2^3*1024*1024<=8m)

region_size=2^23=8m(region_size_log左移1位,也就是2^23)

region_size=8m(MIN_REGION_SIZE(1m)<= 8m <=MAX_REGION_SIZE(32m))


源码下载地址:

https://codeload.github.com/AdoptOpenJDK/openjdk-jdk11u

### JVM垃圾回收器种类及其作用 #### 1. **Serial 收集器** `Serial` 是最基础的垃圾收集器之一,主要用于新生代垃圾回收。它是单线程工作的,在垃圾回收期间会触发 Stop-The-World (STW),即暂停所有用户线程。这种收集器适用于小型应用程序或嵌入式设备中的客户端模式下运行的虚拟机[^1]。 优点在于其实现简单且高效(针对单线程环境),但在现代多核处理器环境中表现不佳。 ```java // 启动 Serial GC 参数 -XX:+UseSerialGC ``` --- #### 2. **ParNew 收集器** `ParNew` 是 `Serial` 的多线程版本,专门用于新生代垃圾回收。它同样会在垃圾回收时引发 STW,但通过并行处理多个线程可以显著缩短停顿时间。因此,相比于 `Serial`,它更适配于具有多核 CPU 的服务器端应用[^3]。 虽然提高了效率,但如果堆内存较大,则仍可能出现较长的停顿时间。 ```java // 启动 ParNew GC 参数 -XX:+UseParNewGC ``` --- #### 3. **Parallel Scavenge (吞吐优先)** 这是一种专注于提升系统整体吞吐量的新生代垃圾收集器。与 `ParNew` 类似,它也是多线程运作;然而,不同于后者关注最小化停顿时间的设计理念,`Parallel Scavenge` 致力于最大化程序执行的有效时间比例[^4]。 特别适合那些对响应速度要求不高但希望获得更高性能的应用场合,例如后台批量处理任务。 ```java // 启动 Parallel Scavenge GC 参数 -XX:+UseParallelGC ``` --- #### 4. **CMS (Concurrent Mark-Sweep)** `CMS` (Concurrent Mark Sweep)是一个面向老年代的垃圾收集器,旨在提供较低的延迟体验。其核心策略是尽可能让大部分垃圾回收动作与用户的正常业务逻辑并发执行,从而减少因 STW 导致的服务中断现象[^4]。 不过,由于采用了标记-清除算法而非复制算法,容易造成内存碎片问题,进而可能需要依赖额外的 Full GC 来解决问题。 ```java // 启动 CMS GC 参数 -XX:+UseConcMarkSweepGC ``` --- #### 5. **G1 (Garbage First)** 作为一款现代化的垃圾收集器,`G1` 将整个 Java 堆划分为许多大小相等的小块区域 (`Region`),并通过预测模型来决定何时以及如何进行局部或者全局范围内的垃圾回收活动[^4]。 相比传统的分代式方法,这种方式不仅有助于更好地平衡各部分之间的负载压力,还支持更加精细地设定期望的最大停顿时间目标。 ```java // 启动 G1 GC 参数 -XX:+UseG1GC ``` --- #### 总结表 | 不同垃圾回收器的作用对比 | 名称 | 主要用途 | 特点 | |--------------------|--------------------|------------------------------------------------------------------------------------------| | Serial | 客户端模式 | 单线程、简单高效 | | ParNew | 新生代, 多核 | 多线程版 Serial,降低 STW 时间 | | Parallel Scavenge | 高吞吐量需求 | 注重吞吐量优化 | | CMS | 老年代, 低延迟 | 减少停顿时间 | | G1 | 综合优化 | 动态调整、分区管理 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值