JVM 内存与 GC 实战:堆/栈/元空间、对象生命周期、G1/ZGC/GenZGC 概览
大约 3 分钟
JVM 内存与 GC 实战:堆/栈/元空间、对象生命周期、G1/ZGC/GenZGC 概览
新手一屏速览
- 对象大多在新生代分配,短命对象在 Minor GC 回收;逃逸分析与标量替换减少分配
- G1 面向大堆与低停顿,ZGC 进一步降低停顿;调优先观测后设定目标,而非盲拍参数
- JFR/GC 日志/指标三位一体观测,结合业务负载画像调参
1. 运行时区域与对象分配
- 线程栈、堆(新生代/老年代)、TLAB、元空间、代码缓存
- 对象在 TLAB 分配为主;大对象/长期存活可能直接晋升老年代
2. GC 算法与实现
- 分代假设:大部分对象朝生夕死
- 算法:标记-清除/复制/标记-整理;写屏障与卡表维护代际关系
3. G1 概览
- 基于 Region 的分区堆;年轻/混合收集;预测性停顿目标(-XX:MaxGCPauseMillis)
- 关键参数与观测:Region 大小、并发线程、晋升失败、重标记与混合周期
4. ZGC/GenZGC 概览
- ZGC:并发标记与重定位、染色指针,低停顿;适合大堆与延迟敏感
- GenZGC:在 ZGC 基础上引入分代,进一步提升吞吐/延迟均衡
5. 观测与调优路径
最近建一些几十个工作内推群,各大城市都有,群里目前已经收集了很多内推岗位,大厂、中厂、小厂、外包都有。 欢迎HR、开发、测试、运维和产品加入。

扫描下方微信,备注:网站+所在城市,即可拉你进工作内推群。

- 开启统一 GC 日志,配合 JFR 事件与指标系统(分配速率/晋升/停顿分布)
- 调优顺序:设定目标 → 观测瓶颈(分配/晋升/碎片/引用)→ 局部参数试探 → 回归验证
6. 常见问题与策略
- 频繁 Full GC:检查晋升失败/大对象/缓存过大;降低分配或增大内存/调整阈值
- 停顿长:降低老年代压力,检查混合周期与晋升带宽;必要时评估 ZGC
7. 练习
- 在 G1 上设置不同停顿目标,压测并输出停顿 P50/P95/P99 对比
- 对一段对象密集代码进行逃逸分析与标量替换改写,比较吞吐与分配速率
示例代码(可直接复制运行)
示例一:对象分配压力与短命对象
import java.util.*;
public class AllocationChurn {
static class Node { byte[] pad = new byte[256]; }
public static void main(String[] args) {
List<Node> survive = new ArrayList<>();
for (int r=0;r<50;r++) {
for (int i=0;i<100_000;i++) new Node(); // 短命对象,易在 Minor GC 回收
for (int i=0;i<1000;i++) survive.add(new Node()); // 部分存活,可能晋升
if (survive.size() > 50_000) survive.subList(0, 25_000).clear();
}
System.out.println("live=" + survive.size());
// 运行时可添加 JVM 参数观察:-Xms512m -Xmx512m -XX:+UseG1GC -Xlog:gc*
}
}示例二:逃逸分析与标量替换(示意)
// 在默认 JVM 下编译运行,JIT 可将 Point 标量化,减少分配
public class EscapeAnalysis {
static class Point { int x, y; }
static int hotLoop(int n){
int acc = 0;
for (int i=0;i<n;i++){
Point p = new Point(); // 局部对象不逃逸
p.x = i; p.y = i * 2;
acc += p.x + p.y;
}
return acc;
}
public static void main(String[] args) {
System.out.println(hotLoop(10_000_000));
// 可对比将 Point 存入全局集合的版本,观察分配/吞吐差异
}
}示例三:引用类型(软/弱)示例
import java.lang.ref.*;
import java.util.concurrent.TimeUnit;
public class RefTypes {
public static void main(String[] args) throws Exception {
SoftReference<byte[]> soft = new SoftReference<>(new byte[10_000_000]);
WeakReference<Object> weak = new WeakReference<>(new Object());
System.out.println("soft get? " + (soft.get()!=null));
System.out.println("weak get? " + (weak.get()!=null));
System.gc(); // 提示 GC
TimeUnit.MILLISECONDS.sleep(100);
System.out.println("after GC soft? " + (soft.get()!=null));
System.out.println("after GC weak? " + (weak.get()!=null));
}
}