G1垃圾回收器的Copying算法是其新生代回收和混合回收阶段的核心实现逻辑,当堆内存存在大量碎片化空间时,该算法的执行效率会受到影响,进而延长Stop The World的暂停时间。分析这一场景下的具体表现,需要从日志采集、指标提取、场景模拟三个维度展开。
核心概念铺垫
首先需要明确几个基础概念,避免分析过程中出现认知偏差:
- G1的Copying算法:将存活对象从源Region复制到空闲Region,同时回收源Region的空间,是G1实现内存整理的核心方式。
- 大内存堆碎片化:指堆中存在大量不连续的小空闲块,无法满足大对象分配需求,同时增加Copying阶段寻找空闲Region的成本。
- Stop The World:GC过程中应用线程暂停的时间段,Copying算法的执行全程处于STW状态,其耗时直接决定GC对应用的影响程度。
GC日志关键指标提取
分析Stop The World表现的第一步是获取完整的G1 GC日志,需要在JVM启动参数中添加以下配置:
-XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintTenuringDistribution -XX:+PrintAdaptiveSizePolicy -Xloggc:/path/to/gc.log
在日志中,Copying算法相关的STW表现可以通过以下关键字段判断:
| 日志字段 | 含义 | 与碎片化的关联 |
|---|---|---|
| GC pause (G1 Evacuation Pause) | Copying阶段触发的STW暂停 | 碎片化严重时该类型暂停频率会升高 |
| Eden: 1024.0M(1024.0M)->0.0B(1024.0M) | Eden区回收前后的变化 | 碎片化导致Eden区可用空间不足时会频繁触发 |
| Survivor: 128.0M->128.0M | 幸存区大小变化 | 碎片化会导致幸存区对象复制耗时增加 |
| Times: user=0.45 sys=0.02, real=0.03 secs | STW实际耗时 | 碎片化严重时real值会明显上升 |
模拟大内存堆碎片化场景
可以通过代码模拟碎片化场景,观测Copying算法的STW表现,示例代码如下:
import java.util.ArrayList;
import java.util.List;
public class G1FragmentationDemo {
// 定义小对象类,模拟大量小对象占用空间
static class SmallObject {
private byte[] data = new byte[1024]; // 每个对象约1KB
}
// 定义大对象类,模拟大对象分配失败触发碎片化整理
static class LargeObject {
private byte[] data = new byte[1024 * 1024 * 10]; // 每个对象约10MB
}
public static void main(String[] args) throws InterruptedException {
List<SmallObject> smallObjects = new ArrayList<>();
List<LargeObject> largeObjects = new ArrayList<>();
// 第一步:分配大量小对象,填满堆空间,形成碎片化
for (int i = 0; i < 80000; i++) {
smallObjects.add(new SmallObject());
if (i % 10000 == 0) {
Thread.sleep(100);
}
}
// 第二步:随机释放部分小对象,留下不连续的空闲空间
for (int i = 0; i < smallObjects.size(); i += 2) {
smallObjects.set(i, null);
}
// 第三步:尝试分配大对象,触发G1的Copying算法整理碎片化空间
for (int i = 0; i < 20; i++) {
try {
largeObjects.add(new LargeObject());
System.out.println("成功分配第" + (i + 1) + "个大对象");
} catch (OutOfMemoryError e) {
System.out.println("分配大对象失败,触发Full GC");
}
Thread.sleep(500);
}
}
}
运行该代码时,搭配前面提到的GC日志参数,就可以在日志中看到碎片化场景下Copying阶段的STW耗时变化。如果堆碎片化严重,会出现以下特征:
- Evacuation Pause的real耗时从正常的几毫秒上升到几十甚至上百毫秒
- GC日志中出现大量
to-space exhausted提示,说明Copying阶段找不到足够的空闲Region - 混合回收阶段的STW耗时占比明显升高
进阶分析工具使用
除了日志分析,还可以使用工具量化Stop The World的表现:
GCViewer
将GC日志导入GCViewer后,可以直观看到STW暂停的时间分布曲线,碎片化场景下曲线会出现明显的高点,同时可以统计平均STW耗时、最大STW耗时等指标。
JFR(Java Flight Recorder)
开启JFR后可以采集更细粒度的GC事件数据,包括Copying阶段每个子步骤的耗时,比如存活对象扫描耗时、对象复制耗时、Region整理耗时,能够定位碎片化具体影响的是Copying的哪个环节。
优化方向参考
如果分析发现碎片化导致Copying算法的STW表现不符合预期,可以尝试以下优化方向:
- 调整
-XX:G1HeapRegionSize参数,增大Region大小,减少碎片化出现的概率 - 设置合理的
-XX:MaxGCPauseMillis值,避免G1为了追求暂停时间而留下过多碎片 - 避免应用中频繁分配短生命周期的大对象,减少Copying阶段的压力
注意分析时需要排除其他GC事件的影响,比如并发标记阶段的耗时不会计入STW,需要单独区分Evacuation Pause的耗时数据,才能得到Copying算法的真实表现。
G1垃圾回收器Copying_算法大内存堆碎片化Stop_The_World修改时间:2026-07-05 12:36:34