版本基线
| 类别 | 版本 |
|---|---|
| Java | 17 (LTS) |
| JVM | HotSpot 17 |
| Spring Boot | 3.2.x |
| 主流 GC | G1(默认)/ ZGC(低延迟) |
| 监控 | Prometheus + Grafana |
选型口诀:
- 堆 < 4G + 开发测试:Serial / Parallel
- 4G ≤ 堆 < 32G:G1(JDK 9+ 默认)
- 堆 ≥ 32G + 超低延迟:ZGC(< 1ms 暂停)
- 堆 ≥ 32G + 兼容性优先:Shenandoah
1、GC 基础概念
1.1 什么是 GC
GC = Garbage Collection ,Java 内存自动回收机制。区别于 C/C++ 的手动 malloc / free,Java 程序员只管 new,回收由 JVM 完成。
C# .NET CLR 也带 GC(与 JVM 同源思路,但分代策略不同:CLR 同样分新生代 / 老年代,且支持 workstation / server 两种模式)。
.NET 对照:
| 维度 | JVM | .NET CLR |
|---|---|---|
| 分代 | 新生代 / 老年代(G1 后变为 Region) | Gen 0 / Gen 1 / Gen 2 + LOH |
| 默认 GC | G1(JDK 9+) | Workstation GC / Server GC |
| 手动触发 | System.gc()(仅建议) |
GC.Collect()(强制) |
| 调优入口 | -Xmx / -XX:+UseG1GC |
<gcServer> / <gcConcurrent> |
重点 :Java 的 GC 是 建议式 (System.gc 不可保证),而 .NET 的 GC.Collect 是 强制式。这是两个 GC 模型最大的语义差异。
1.2 引用类型
JVM 提供 4 种引用强度,控制对象的生命周期。
| 类型 | 回收时机 | 典型用途 |
|---|---|---|
| 强引用 Strong | 永不回收(OOM 也保留) | 普通对象 |
| 软引用 Soft | 内存不足时回收(OOM 前清空) | 缓存(图片缓存 / 数据缓存) |
| 弱引用 Weak | 下次 GC 必回收 | WeakHashMap / ThreadLocal 防内存泄漏 |
| 虚引用 Phantom | 不影响生命周期,仅跟踪回收 | 直接内存回收跟踪(Cleaner) |
代码示意:
java
// 1. 强引用 ------ 永远不会回收
Object obj = new Object();
// 2. 软引用 ------ 内存不够时回收
SoftReference<byte[]> softRef = new SoftReference<>(new byte[1024 * 1024]);
byte[] data = softRef.get(); // 可能为 null
// 3. 弱引用 ------ 下次 GC 必回收
WeakReference<Object> weakRef = new WeakReference<>(new Object());
// 4. 虚引用 ------ 必须配合 ReferenceQueue
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);
实战坑 :ThreadLocal 不清理会内存泄漏。线程池的 core 线程存活时间长 → ThreadLocal 的 Entry(WeakReference)虽然 key 被回收,但 value 仍是强引用 → 必须调用 remove()。
.NET 对照:
| 类型 | JVM | .NET CLR |
|---|---|---|
| 强引用 | Object obj = new Object() |
object obj = new object() |
| 软引用 | SoftReference<T> |
WeakReference<T>(无软引用) |
| 弱引用 | WeakReference<T> |
WeakReference<T>(类似) |
| 虚引用 | PhantomReference<T> |
无对应(ConditionalWeakTable 替代) |
| 终结器 | Object.finalize() |
~Object() / IDisposable |
重点 :.NET 的
WeakReference更接近 Java 的SoftReference(同等内存压力下回收)。.NET 鼓励显式实现IDisposable+using(确定性释放),避免依赖 GC。
1.3 内存区域
#mermaid-svg-L7BzR2TtttwpRjdh{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-L7BzR2TtttwpRjdh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-L7BzR2TtttwpRjdh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-L7BzR2TtttwpRjdh .error-icon{fill:#552222;}#mermaid-svg-L7BzR2TtttwpRjdh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-L7BzR2TtttwpRjdh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-L7BzR2TtttwpRjdh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-L7BzR2TtttwpRjdh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-L7BzR2TtttwpRjdh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-L7BzR2TtttwpRjdh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-L7BzR2TtttwpRjdh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-L7BzR2TtttwpRjdh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-L7BzR2TtttwpRjdh .marker.cross{stroke:#333333;}#mermaid-svg-L7BzR2TtttwpRjdh svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-L7BzR2TtttwpRjdh p{margin:0;}#mermaid-svg-L7BzR2TtttwpRjdh .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-L7BzR2TtttwpRjdh .cluster-label text{fill:#333;}#mermaid-svg-L7BzR2TtttwpRjdh .cluster-label span{color:#333;}#mermaid-svg-L7BzR2TtttwpRjdh .cluster-label span p{background-color:transparent;}#mermaid-svg-L7BzR2TtttwpRjdh .label text,#mermaid-svg-L7BzR2TtttwpRjdh span{fill:#333;color:#333;}#mermaid-svg-L7BzR2TtttwpRjdh .node rect,#mermaid-svg-L7BzR2TtttwpRjdh .node circle,#mermaid-svg-L7BzR2TtttwpRjdh .node ellipse,#mermaid-svg-L7BzR2TtttwpRjdh .node polygon,#mermaid-svg-L7BzR2TtttwpRjdh .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-L7BzR2TtttwpRjdh .rough-node .label text,#mermaid-svg-L7BzR2TtttwpRjdh .node .label text,#mermaid-svg-L7BzR2TtttwpRjdh .image-shape .label,#mermaid-svg-L7BzR2TtttwpRjdh .icon-shape .label{text-anchor:middle;}#mermaid-svg-L7BzR2TtttwpRjdh .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-L7BzR2TtttwpRjdh .rough-node .label,#mermaid-svg-L7BzR2TtttwpRjdh .node .label,#mermaid-svg-L7BzR2TtttwpRjdh .image-shape .label,#mermaid-svg-L7BzR2TtttwpRjdh .icon-shape .label{text-align:center;}#mermaid-svg-L7BzR2TtttwpRjdh .node.clickable{cursor:pointer;}#mermaid-svg-L7BzR2TtttwpRjdh .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-L7BzR2TtttwpRjdh .arrowheadPath{fill:#333333;}#mermaid-svg-L7BzR2TtttwpRjdh .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-L7BzR2TtttwpRjdh .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-L7BzR2TtttwpRjdh .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-L7BzR2TtttwpRjdh .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-L7BzR2TtttwpRjdh .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-L7BzR2TtttwpRjdh .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-L7BzR2TtttwpRjdh .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-L7BzR2TtttwpRjdh .cluster text{fill:#333;}#mermaid-svg-L7BzR2TtttwpRjdh .cluster span{color:#333;}#mermaid-svg-L7BzR2TtttwpRjdh div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-L7BzR2TtttwpRjdh .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-L7BzR2TtttwpRjdh rect.text{fill:none;stroke-width:0;}#mermaid-svg-L7BzR2TtttwpRjdh .icon-shape,#mermaid-svg-L7BzR2TtttwpRjdh .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-L7BzR2TtttwpRjdh .icon-shape p,#mermaid-svg-L7BzR2TtttwpRjdh .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-L7BzR2TtttwpRjdh .icon-shape .label rect,#mermaid-svg-L7BzR2TtttwpRjdh .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-L7BzR2TtttwpRjdh .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-L7BzR2TtttwpRjdh .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-L7BzR2TtttwpRjdh :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} JVM 内存
堆 Heap
栈 Stack
方法区 Metaspace
直接内存 DirectMemory
新生代 Young
老年代 Old
Eden
Survivor S0
Survivor S1
.NET 对照(CLR 内存区域):
| 维度 | JVM | .NET CLR |
|---|---|---|
| 堆 | Heap(Young + Old) | Managed Heap(Gen 0/1/2 + LOH) |
| 栈 | Thread Stack | Thread Stack |
| 方法区 | Metaspace(JDK 8+) | Loader Heap / JIT 代码 |
| 堆外 | DirectBuffer | Unmanaged Memory(Marshal.AllocHGlobal) |
| 显式释放 | -XX:MaxDirectMemorySize + Cleaner |
IDisposable + Dispose |
重点 :.NET 没有"直接内存"概念------通过
Marshal.AllocHGlobal申请非托管内存,必须 手动FreeHGlobal释放。Java 的DirectByteBuffer靠Cleaner(虚引用)自动回收,更优雅。
各区域关键参数(JDK 17 默认):
| 区域 | 线程共享 | 抛错 | 配置参数 |
|---|---|---|---|
| 堆 Heap | 共享 | OOM: Java heap space | -Xms / -Xmx |
| 栈 Stack | 私有 | StackOverflowError | -Xss(默认 1M) |
| 方法区 Metaspace | 共享 | OOM: Metaspace | -XX:MetaspaceSize / -XX:MaxMetaspaceSize |
| 直接内存 Direct | 共享 | OOM: Direct buffer memory | -XX:MaxDirectMemorySize |
直接内存 是 NIO / Netty 的核心:堆外内存、不受 GC 管理、由 Cleaner(虚引用)跟踪回收。坑点:配置不当会 OOM,且不显示在堆 dump 中。
1.4 对象生命周期
一个对象的标准生命周期:
创建 → Eden 分配 → Minor GC(存活 → Survivor) → 多次 Minor GC(年龄 +1)
→ 年龄超阈值(默认 15)→ 老年代 → Full GC → OOM
#mermaid-svg-3tvhla8RDVzYn4zp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3tvhla8RDVzYn4zp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3tvhla8RDVzYn4zp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3tvhla8RDVzYn4zp .error-icon{fill:#552222;}#mermaid-svg-3tvhla8RDVzYn4zp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3tvhla8RDVzYn4zp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3tvhla8RDVzYn4zp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3tvhla8RDVzYn4zp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3tvhla8RDVzYn4zp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3tvhla8RDVzYn4zp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3tvhla8RDVzYn4zp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3tvhla8RDVzYn4zp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3tvhla8RDVzYn4zp .marker.cross{stroke:#333333;}#mermaid-svg-3tvhla8RDVzYn4zp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3tvhla8RDVzYn4zp p{margin:0;}#mermaid-svg-3tvhla8RDVzYn4zp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3tvhla8RDVzYn4zp .cluster-label text{fill:#333;}#mermaid-svg-3tvhla8RDVzYn4zp .cluster-label span{color:#333;}#mermaid-svg-3tvhla8RDVzYn4zp .cluster-label span p{background-color:transparent;}#mermaid-svg-3tvhla8RDVzYn4zp .label text,#mermaid-svg-3tvhla8RDVzYn4zp span{fill:#333;color:#333;}#mermaid-svg-3tvhla8RDVzYn4zp .node rect,#mermaid-svg-3tvhla8RDVzYn4zp .node circle,#mermaid-svg-3tvhla8RDVzYn4zp .node ellipse,#mermaid-svg-3tvhla8RDVzYn4zp .node polygon,#mermaid-svg-3tvhla8RDVzYn4zp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3tvhla8RDVzYn4zp .rough-node .label text,#mermaid-svg-3tvhla8RDVzYn4zp .node .label text,#mermaid-svg-3tvhla8RDVzYn4zp .image-shape .label,#mermaid-svg-3tvhla8RDVzYn4zp .icon-shape .label{text-anchor:middle;}#mermaid-svg-3tvhla8RDVzYn4zp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3tvhla8RDVzYn4zp .rough-node .label,#mermaid-svg-3tvhla8RDVzYn4zp .node .label,#mermaid-svg-3tvhla8RDVzYn4zp .image-shape .label,#mermaid-svg-3tvhla8RDVzYn4zp .icon-shape .label{text-align:center;}#mermaid-svg-3tvhla8RDVzYn4zp .node.clickable{cursor:pointer;}#mermaid-svg-3tvhla8RDVzYn4zp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3tvhla8RDVzYn4zp .arrowheadPath{fill:#333333;}#mermaid-svg-3tvhla8RDVzYn4zp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3tvhla8RDVzYn4zp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3tvhla8RDVzYn4zp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3tvhla8RDVzYn4zp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3tvhla8RDVzYn4zp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3tvhla8RDVzYn4zp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3tvhla8RDVzYn4zp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3tvhla8RDVzYn4zp .cluster text{fill:#333;}#mermaid-svg-3tvhla8RDVzYn4zp .cluster span{color:#333;}#mermaid-svg-3tvhla8RDVzYn4zp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3tvhla8RDVzYn4zp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3tvhla8RDVzYn4zp rect.text{fill:none;stroke-width:0;}#mermaid-svg-3tvhla8RDVzYn4zp .icon-shape,#mermaid-svg-3tvhla8RDVzYn4zp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3tvhla8RDVzYn4zp .icon-shape p,#mermaid-svg-3tvhla8RDVzYn4zp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3tvhla8RDVzYn4zp .icon-shape .label rect,#mermaid-svg-3tvhla8RDVzYn4zp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3tvhla8RDVzYn4zp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3tvhla8RDVzYn4zp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3tvhla8RDVzYn4zp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Minor GC
Minor GC
年龄 15
Full GC
空间不足
new byte 1024
Eden 区分配
Survivor S0
Survivor S1
老年代
回收
OOM 抛出
.NET 对照:
// C# - 显式释放(确定性)
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
// 立即释放(不依赖 GC)
}
// Java - 隐式释放(依赖 GC)
try (Connection conn = DriverManager.getConnection(url)) {
// try-with-resources(Java 7+ 也支持确定性)
}
重点 :.NET 推荐
IDisposable+using(确定性释放),适合资源密集型场景(DB / 文件 / Socket)。Java 也支持 try-with-resources,但 GC Root 扫描仍依赖 JVM。
年龄阈值 :-XX:MaxTenuringThreshold=15(默认 15,CMS 是 6)。G1 因为 Region 化,最大可达 15。
晋升老年代的 4 种触发:
- 年龄超阈值(默认 15)
- Survivor 区相同年龄对象总大小 > Survivor 50% → 直接晋升
- 大对象直接进老年代(
-XX:PretenureSizeThreshold) - Minor GC 后存活对象超过 Survivor 容量 → 提前晋升
1.5 GC 触发条件
| GC 类型 | 触发条件 | 范围 | 暂停 |
|---|---|---|---|
| Minor GC | Eden 区满 | 新生代 | 短(10-100ms) |
| Major GC | 老年代满 | 老年代 | 中(100ms-1s) |
| Mixed GC | G1 特有 | 部分老年代 + 新生代 | 可控 |
| Full GC | 整堆 + Metaspace | 整堆 | 长(秒级) |
| OOM | 堆耗尽 | 整堆 | 致命 |
System.gc() 仅是 建议 ,JVM 可以通过 -XX:+DisableExplicitGC 禁用。生产环境建议禁用------它会触发 Full GC。
.NET 对照:
| GC 类型 | JVM | .NET CLR |
|---|---|---|
| Minor | Eden 满 | Gen 0 满 |
| Major | Old 满 | Gen 2 满 |
| Full | 整堆 | GC.Collect() 强制触发(Workstation/Server) |
重点 :.NET 的
GC.Collect()是 强制式 (同步阻塞到完成),Java 的System.gc()是 建议式 (JVM 可忽略)。生产中 .NET 极少手动调GC.Collect()(让 CLR 自动管理)。
1.6 FAQ
- Q1:强引用 / 软引用 / 弱引用 / 虚引用的区别?
回收强度递减。软引用适合做缓存(OOM 前清),弱引用适合 ThreadLocal 防泄漏(key 自动回收),虚引用必须配合 ReferenceQueue 用------典型场景是 DirectByteBuffer 的 Cleaner。 - Q2:JVM 内存区域划分?堆 / 栈 / 方法区的区别?
堆线程共享(对象实例);栈线程私有(局部变量 / 方法栈帧);方法区共享(类元数据 / 常量池,JDK 8 后叫 Metaspace)。直接内存是堆外,但 NIO 经常用。 - Q3:Minor GC / Major GC / Full GC 触发条件?
Minor = Eden 满;Major = Old 满(CMS 术语,HotSpot 已淡化);Full GC = 整堆回收(System.gc / 老年代满 / Metaspace 满 / 晋升失败)。 - Q4:System.gc() 一定会执行 GC 吗?
不一定。JVM 可以通过-XX:+DisableExplicitGC禁用,RMI / NIO 内部也常触发。生产建议加-XX:+DisableExplicitGC避免意外 Full GC。
2、GC 算法与收集器
2.1 GC 算法 4 大基础
#mermaid-svg-zg2rH6GY3riq39lw{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zg2rH6GY3riq39lw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zg2rH6GY3riq39lw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zg2rH6GY3riq39lw .error-icon{fill:#552222;}#mermaid-svg-zg2rH6GY3riq39lw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zg2rH6GY3riq39lw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zg2rH6GY3riq39lw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zg2rH6GY3riq39lw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zg2rH6GY3riq39lw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zg2rH6GY3riq39lw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zg2rH6GY3riq39lw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zg2rH6GY3riq39lw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zg2rH6GY3riq39lw .marker.cross{stroke:#333333;}#mermaid-svg-zg2rH6GY3riq39lw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zg2rH6GY3riq39lw p{margin:0;}#mermaid-svg-zg2rH6GY3riq39lw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zg2rH6GY3riq39lw .cluster-label text{fill:#333;}#mermaid-svg-zg2rH6GY3riq39lw .cluster-label span{color:#333;}#mermaid-svg-zg2rH6GY3riq39lw .cluster-label span p{background-color:transparent;}#mermaid-svg-zg2rH6GY3riq39lw .label text,#mermaid-svg-zg2rH6GY3riq39lw span{fill:#333;color:#333;}#mermaid-svg-zg2rH6GY3riq39lw .node rect,#mermaid-svg-zg2rH6GY3riq39lw .node circle,#mermaid-svg-zg2rH6GY3riq39lw .node ellipse,#mermaid-svg-zg2rH6GY3riq39lw .node polygon,#mermaid-svg-zg2rH6GY3riq39lw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zg2rH6GY3riq39lw .rough-node .label text,#mermaid-svg-zg2rH6GY3riq39lw .node .label text,#mermaid-svg-zg2rH6GY3riq39lw .image-shape .label,#mermaid-svg-zg2rH6GY3riq39lw .icon-shape .label{text-anchor:middle;}#mermaid-svg-zg2rH6GY3riq39lw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zg2rH6GY3riq39lw .rough-node .label,#mermaid-svg-zg2rH6GY3riq39lw .node .label,#mermaid-svg-zg2rH6GY3riq39lw .image-shape .label,#mermaid-svg-zg2rH6GY3riq39lw .icon-shape .label{text-align:center;}#mermaid-svg-zg2rH6GY3riq39lw .node.clickable{cursor:pointer;}#mermaid-svg-zg2rH6GY3riq39lw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zg2rH6GY3riq39lw .arrowheadPath{fill:#333333;}#mermaid-svg-zg2rH6GY3riq39lw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zg2rH6GY3riq39lw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zg2rH6GY3riq39lw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zg2rH6GY3riq39lw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zg2rH6GY3riq39lw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zg2rH6GY3riq39lw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zg2rH6GY3riq39lw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zg2rH6GY3riq39lw .cluster text{fill:#333;}#mermaid-svg-zg2rH6GY3riq39lw .cluster span{color:#333;}#mermaid-svg-zg2rH6GY3riq39lw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zg2rH6GY3riq39lw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zg2rH6GY3riq39lw rect.text{fill:none;stroke-width:0;}#mermaid-svg-zg2rH6GY3riq39lw .icon-shape,#mermaid-svg-zg2rH6GY3riq39lw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zg2rH6GY3riq39lw .icon-shape p,#mermaid-svg-zg2rH6GY3riq39lw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zg2rH6GY3riq39lw .icon-shape .label rect,#mermaid-svg-zg2rH6GY3riq39lw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zg2rH6GY3riq39lw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zg2rH6GY3riq39lw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zg2rH6GY3riq39lw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} GC 算法
标记-清除
标记-整理
复制算法
分代收集
.NET 对照:
| 算法 | JVM | .NET CLR |
|---|---|---|
| 标记-清除 | CMS | Gen 2(Workstation GC 用) |
| 标记-整理 | Serial Old | Server GC(压缩代) |
| 复制 | 新生代 | Gen 0 / Gen 1 |
| 分代收集 | G1 / ZGC | Gen 0/1/2 + LOH |
重点 :.NET CLR 默认 Workstation GC (Gen 0/1 + Gen 2,延迟优先),服务器场景用 Server GC(后台线程并行回收)。.NET 没有"全堆压缩",LOH(大对象堆)永远不压缩。
| 算法 | 原理 | 优点 | 缺点 | 适用 |
|---|---|---|---|---|
| 标记-清除 | 标记存活 → 清除未标记 | 简单 | 内存碎片 | 老年代(CMS) |
| 标记-整理 | 标记存活 → 移动到一端 | 无碎片 | 移动成本 | 老年代(Serial Old / Parallel Old) |
| 复制算法 | 分两半 → 存活复制到另一半 | 无碎片 + 高效 | 内存浪费 50% | 新生代(Serial / ParNew / Parallel Scavenge) |
| 分代收集 | 分新生代 / 老年代分别用不同算法 | 综合最优 | 复杂 | 现代 JVM 全部采用 |
算法选择经验:
- 新生代对象朝生夕死 → 复制算法最高效(浪费 50% 内存换来无碎片)
- 老年代对象存活率高 → 标记-清除 / 标记-整理(复制代价太大)
- G1 把堆拆 Region 后,每个 Region 可单独选算法
2.2 7 大垃圾收集器
| 收集器 | 算法 | 适用 | JDK 版本 | 状态 |
|---|---|---|---|---|
| Serial | 复制 + 标记-整理 | 客户端 / 单核 | 1.3+ | 维护 |
| ParNew | 复制 | 新生代 / 多核 | 1.4+ | CMS 配套 |
| Parallel Scavenge | 复制 | 吞吐量优先 | 1.4+ | 维护 |
| CMS | 标记-清除 | 老年代 / 低延迟 | 5+ | JDK 9 弃用,14 移除 |
| G1 | 标记-整理 + 复制 | 全堆 / 低延迟 | 7u4+ | JDK 9+ 默认 |
| ZGC | 染色指针 + 读屏障 | TB 级堆 / < 1ms 暂停 | 11+ 实验,15+ 正式 | 主推 |
| Shenandoah | 转发指针 + 读屏障 | 与 ZGC 类似 | 12+ | 主推 |
#mermaid-svg-BsoenAKzWplmP2pj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BsoenAKzWplmP2pj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BsoenAKzWplmP2pj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BsoenAKzWplmP2pj .error-icon{fill:#552222;}#mermaid-svg-BsoenAKzWplmP2pj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BsoenAKzWplmP2pj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BsoenAKzWplmP2pj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BsoenAKzWplmP2pj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BsoenAKzWplmP2pj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BsoenAKzWplmP2pj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BsoenAKzWplmP2pj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BsoenAKzWplmP2pj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BsoenAKzWplmP2pj .marker.cross{stroke:#333333;}#mermaid-svg-BsoenAKzWplmP2pj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BsoenAKzWplmP2pj p{margin:0;}#mermaid-svg-BsoenAKzWplmP2pj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BsoenAKzWplmP2pj .cluster-label text{fill:#333;}#mermaid-svg-BsoenAKzWplmP2pj .cluster-label span{color:#333;}#mermaid-svg-BsoenAKzWplmP2pj .cluster-label span p{background-color:transparent;}#mermaid-svg-BsoenAKzWplmP2pj .label text,#mermaid-svg-BsoenAKzWplmP2pj span{fill:#333;color:#333;}#mermaid-svg-BsoenAKzWplmP2pj .node rect,#mermaid-svg-BsoenAKzWplmP2pj .node circle,#mermaid-svg-BsoenAKzWplmP2pj .node ellipse,#mermaid-svg-BsoenAKzWplmP2pj .node polygon,#mermaid-svg-BsoenAKzWplmP2pj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BsoenAKzWplmP2pj .rough-node .label text,#mermaid-svg-BsoenAKzWplmP2pj .node .label text,#mermaid-svg-BsoenAKzWplmP2pj .image-shape .label,#mermaid-svg-BsoenAKzWplmP2pj .icon-shape .label{text-anchor:middle;}#mermaid-svg-BsoenAKzWplmP2pj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BsoenAKzWplmP2pj .rough-node .label,#mermaid-svg-BsoenAKzWplmP2pj .node .label,#mermaid-svg-BsoenAKzWplmP2pj .image-shape .label,#mermaid-svg-BsoenAKzWplmP2pj .icon-shape .label{text-align:center;}#mermaid-svg-BsoenAKzWplmP2pj .node.clickable{cursor:pointer;}#mermaid-svg-BsoenAKzWplmP2pj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BsoenAKzWplmP2pj .arrowheadPath{fill:#333333;}#mermaid-svg-BsoenAKzWplmP2pj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BsoenAKzWplmP2pj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BsoenAKzWplmP2pj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BsoenAKzWplmP2pj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BsoenAKzWplmP2pj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BsoenAKzWplmP2pj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BsoenAKzWplmP2pj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BsoenAKzWplmP2pj .cluster text{fill:#333;}#mermaid-svg-BsoenAKzWplmP2pj .cluster span{color:#333;}#mermaid-svg-BsoenAKzWplmP2pj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BsoenAKzWplmP2pj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BsoenAKzWplmP2pj rect.text{fill:none;stroke-width:0;}#mermaid-svg-BsoenAKzWplmP2pj .icon-shape,#mermaid-svg-BsoenAKzWplmP2pj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BsoenAKzWplmP2pj .icon-shape p,#mermaid-svg-BsoenAKzWplmP2pj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BsoenAKzWplmP2pj .icon-shape .label rect,#mermaid-svg-BsoenAKzWplmP2pj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BsoenAKzWplmP2pj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BsoenAKzWplmP2pj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BsoenAKzWplmP2pj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} GC 收集器演进
Serial 1.3
Parallel 1.4
CMS 5
G1 7u4
ZGC 11
Shenandoah 12
现代组合
新生代: G1 Young
老年代: G1 Mixed
.NET 对照:
| 收集器 | JVM | .NET CLR |
|---|---|---|
| Serial | 1.3+ | 无(CLR 多线程) |
| Parallel | 1.4+ | Server GC(背景 GC 线程) |
| CMS | 5+ | Workstation GC(类似概念) |
| G1 | 7u4+ | 无对应(CLR 用 Gen 0/1/2) |
| ZGC | 11+ | 无对应(.NET 9 有类似但未生产) |
重点 :.NET CLR 没有 G1 / ZGC 这种统一堆的并发收集器 ------CLR 的 Server GC 是 Gen 2 后台线程并行回收。.NET 9 引入了DATAS(Dynamic Allocation and Tiered Allocation Segments) 改进,但仍是分代收集器,没有类似 G1 的区域化设计。
2.3 G1 收集器详解(重点)
G1(Garbage-First)是 JDK 9+ 默认收集器。
核心设计:
- Region 化:堆拆成最多 2048 个 Region(默认 1MB ~ 32MB,按堆大小自动算)
- Garbage-First:优先回收价值最大的 Region(回收空间多 + 回收时间短)
- 停顿可控 :
MaxGCPauseMillis默认 200ms(软目标) - 三色标记 + SATB:并发标记不漏标
#mermaid-svg-8zyp0EQ9ApWRtGft{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8zyp0EQ9ApWRtGft .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8zyp0EQ9ApWRtGft .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8zyp0EQ9ApWRtGft .error-icon{fill:#552222;}#mermaid-svg-8zyp0EQ9ApWRtGft .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8zyp0EQ9ApWRtGft .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8zyp0EQ9ApWRtGft .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8zyp0EQ9ApWRtGft .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8zyp0EQ9ApWRtGft .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8zyp0EQ9ApWRtGft .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8zyp0EQ9ApWRtGft .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8zyp0EQ9ApWRtGft .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8zyp0EQ9ApWRtGft .marker.cross{stroke:#333333;}#mermaid-svg-8zyp0EQ9ApWRtGft svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8zyp0EQ9ApWRtGft p{margin:0;}#mermaid-svg-8zyp0EQ9ApWRtGft .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8zyp0EQ9ApWRtGft .cluster-label text{fill:#333;}#mermaid-svg-8zyp0EQ9ApWRtGft .cluster-label span{color:#333;}#mermaid-svg-8zyp0EQ9ApWRtGft .cluster-label span p{background-color:transparent;}#mermaid-svg-8zyp0EQ9ApWRtGft .label text,#mermaid-svg-8zyp0EQ9ApWRtGft span{fill:#333;color:#333;}#mermaid-svg-8zyp0EQ9ApWRtGft .node rect,#mermaid-svg-8zyp0EQ9ApWRtGft .node circle,#mermaid-svg-8zyp0EQ9ApWRtGft .node ellipse,#mermaid-svg-8zyp0EQ9ApWRtGft .node polygon,#mermaid-svg-8zyp0EQ9ApWRtGft .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8zyp0EQ9ApWRtGft .rough-node .label text,#mermaid-svg-8zyp0EQ9ApWRtGft .node .label text,#mermaid-svg-8zyp0EQ9ApWRtGft .image-shape .label,#mermaid-svg-8zyp0EQ9ApWRtGft .icon-shape .label{text-anchor:middle;}#mermaid-svg-8zyp0EQ9ApWRtGft .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8zyp0EQ9ApWRtGft .rough-node .label,#mermaid-svg-8zyp0EQ9ApWRtGft .node .label,#mermaid-svg-8zyp0EQ9ApWRtGft .image-shape .label,#mermaid-svg-8zyp0EQ9ApWRtGft .icon-shape .label{text-align:center;}#mermaid-svg-8zyp0EQ9ApWRtGft .node.clickable{cursor:pointer;}#mermaid-svg-8zyp0EQ9ApWRtGft .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8zyp0EQ9ApWRtGft .arrowheadPath{fill:#333333;}#mermaid-svg-8zyp0EQ9ApWRtGft .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8zyp0EQ9ApWRtGft .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8zyp0EQ9ApWRtGft .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8zyp0EQ9ApWRtGft .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8zyp0EQ9ApWRtGft .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8zyp0EQ9ApWRtGft .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8zyp0EQ9ApWRtGft .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8zyp0EQ9ApWRtGft .cluster text{fill:#333;}#mermaid-svg-8zyp0EQ9ApWRtGft .cluster span{color:#333;}#mermaid-svg-8zyp0EQ9ApWRtGft div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8zyp0EQ9ApWRtGft .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8zyp0EQ9ApWRtGft rect.text{fill:none;stroke-width:0;}#mermaid-svg-8zyp0EQ9ApWRtGft .icon-shape,#mermaid-svg-8zyp0EQ9ApWRtGft .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8zyp0EQ9ApWRtGft .icon-shape p,#mermaid-svg-8zyp0EQ9ApWRtGft .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8zyp0EQ9ApWRtGft .icon-shape .label rect,#mermaid-svg-8zyp0EQ9ApWRtGft .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8zyp0EQ9ApWRtGft .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8zyp0EQ9ApWRtGft .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8zyp0EQ9ApWRtGft :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 堆
Region 0
Region 1
Region N
Eden
Survivor
Old
Humongous 大对象
.NET 对照:
| 维度 | JVM G1 | .NET CLR(Workstation GC) |
|---|---|---|
| 分区 | 2048 个 Region | Gen 0 / Gen 1 / Gen 2(固定 3 代) |
| 停顿目标 | MaxGCPauseMillis=200 |
无显式目标(Workstation 默认低延迟) |
| 大对象 | Humongous Region | LOH(Large Object Heap,85000 字节阈值) |
| 触发 | 堆占用 45% (IHOP) | Gen 2 满 |
重点 :.NET 的 LOH 永远不压缩(避免移动大对象成本),容易碎片。.NET 推荐 ArrayPool 池化 +
GC.AllocateUninitializedArray避免 LOH。Java G1 通过 Humongous Region 解决类似问题。
Region 大小计算 :-XX:G1HeapRegionSize=N(必须是 2 的幂,1MB-32MB)。堆 8G 默认 4MB,堆 16G 默认 8MB。
G1 回收阶段:
- Young GC:回收所有 Eden + Survivor(STW)
- 并发标记:达到 IHOP 阈值(默认 45%)启动
- Mixed GC:回收部分老年代 Region + 新生代(可分多次)
2.4 ZGC 收集器详解
bash
# 启动 ZGC(JDK 17+)
JAVA_OPTS="-XX:+UseZGC -XX:+UseLargePages -Xmx8g"
# Spring Boot 3.x 配 ZGC
java -XX:+UseZGC -XX:+UseLargePages -Xmx8g -jar app.jar
核心原理:
- 染色指针(Colored Pointer):在 64 位指针的高位(4 位)存元数据(Marked0 / Marked1 / Remapped / Finalizable)
- 读屏障(Load Barrier):读取引用时检查指针染色状态,自动修复
- 并发整理:与应用线程并发执行,几乎无 STW
- TB 级堆:支持 8MB ~ 16TB 堆
bash
# 启动 ZGC
JAVA_OPTS="-XX:+UseZGC -XX:+UseLargePages"
# Spring Boot 3.x 配 ZGC
java -XX:+UseZGC -jar app.jar
重点 :ZGC 是 Java 独有------
.NET 9引入了 DATAS 改进但仍是分代收集器,未实现"统一堆并发"。当前 .NET 仍以分代为主,Server GC 在后台线程并行回收。
| 堆大小 | 平均暂停 | 最大暂停 |
|---|---|---|
| 8G | 0.05ms | 0.5ms |
| 64G | 0.1ms | 1ms |
| 1TB | 0.2ms | 2ms |
对比 G1:G1 在 32G 堆时最大暂停可达 100ms+,ZGC 始终保持 < 1ms。
.NET 对照:
| 维度 | JVM ZGC | .NET CLR |
|---|---|---|
| 并发收集 | 几乎全并发 | Server GC Gen 2 后台线程 |
| 染色指针 | 64 位指针高位存元数据 | 无染色指针 |
| 读屏障 | 读取引用时修复 | 无读屏障 |
| 最大暂停 | < 1ms | 几十毫秒(Gen 2 回收) |
| TB 级堆 | 支持 8MB ~ 16TB | 受限(GC 调度) |
重点 :ZGC 是 Java 独有------
.NET CLR 9计划引入类似并发收集器(DOTNET 9 中有 DATAS )。当前 .NET 仍以分代为主,未实现"统一堆并发"。
2.5 GC 收集器选型口诀
#mermaid-svg-AtD0dJksXL9TblwQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AtD0dJksXL9TblwQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AtD0dJksXL9TblwQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AtD0dJksXL9TblwQ .error-icon{fill:#552222;}#mermaid-svg-AtD0dJksXL9TblwQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AtD0dJksXL9TblwQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AtD0dJksXL9TblwQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AtD0dJksXL9TblwQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AtD0dJksXL9TblwQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AtD0dJksXL9TblwQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AtD0dJksXL9TblwQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AtD0dJksXL9TblwQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AtD0dJksXL9TblwQ .marker.cross{stroke:#333333;}#mermaid-svg-AtD0dJksXL9TblwQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AtD0dJksXL9TblwQ p{margin:0;}#mermaid-svg-AtD0dJksXL9TblwQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-AtD0dJksXL9TblwQ .cluster-label text{fill:#333;}#mermaid-svg-AtD0dJksXL9TblwQ .cluster-label span{color:#333;}#mermaid-svg-AtD0dJksXL9TblwQ .cluster-label span p{background-color:transparent;}#mermaid-svg-AtD0dJksXL9TblwQ .label text,#mermaid-svg-AtD0dJksXL9TblwQ span{fill:#333;color:#333;}#mermaid-svg-AtD0dJksXL9TblwQ .node rect,#mermaid-svg-AtD0dJksXL9TblwQ .node circle,#mermaid-svg-AtD0dJksXL9TblwQ .node ellipse,#mermaid-svg-AtD0dJksXL9TblwQ .node polygon,#mermaid-svg-AtD0dJksXL9TblwQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AtD0dJksXL9TblwQ .rough-node .label text,#mermaid-svg-AtD0dJksXL9TblwQ .node .label text,#mermaid-svg-AtD0dJksXL9TblwQ .image-shape .label,#mermaid-svg-AtD0dJksXL9TblwQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-AtD0dJksXL9TblwQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AtD0dJksXL9TblwQ .rough-node .label,#mermaid-svg-AtD0dJksXL9TblwQ .node .label,#mermaid-svg-AtD0dJksXL9TblwQ .image-shape .label,#mermaid-svg-AtD0dJksXL9TblwQ .icon-shape .label{text-align:center;}#mermaid-svg-AtD0dJksXL9TblwQ .node.clickable{cursor:pointer;}#mermaid-svg-AtD0dJksXL9TblwQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AtD0dJksXL9TblwQ .arrowheadPath{fill:#333333;}#mermaid-svg-AtD0dJksXL9TblwQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AtD0dJksXL9TblwQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AtD0dJksXL9TblwQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AtD0dJksXL9TblwQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AtD0dJksXL9TblwQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AtD0dJksXL9TblwQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AtD0dJksXL9TblwQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AtD0dJksXL9TblwQ .cluster text{fill:#333;}#mermaid-svg-AtD0dJksXL9TblwQ .cluster span{color:#333;}#mermaid-svg-AtD0dJksXL9TblwQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AtD0dJksXL9TblwQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AtD0dJksXL9TblwQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-AtD0dJksXL9TblwQ .icon-shape,#mermaid-svg-AtD0dJksXL9TblwQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AtD0dJksXL9TblwQ .icon-shape p,#mermaid-svg-AtD0dJksXL9TblwQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AtD0dJksXL9TblwQ .icon-shape .label rect,#mermaid-svg-AtD0dJksXL9TblwQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AtD0dJksXL9TblwQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AtD0dJksXL9TblwQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AtD0dJksXL9TblwQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} < 4G
4G-32G
≥ 32G
< 1ms
兼容优先
GC 选型决策
堆大小
Serial/Parallel
G1
延迟要求
ZGC
Shenandoah
开发测试 / 单核
JDK 9+ 默认推荐
金融 / 高频交易
RedHat 生态
.NET 对照选型口诀:
应用类型 | JVM 选型 | .NET CLR 选型
客户端 / 桌面 | Serial / Parallel | Workstation GC(默认)
服务端 / Web | G1 | Server GC
低延迟 / 金融 | ZGC | Server GC + 调优 LOH
TB 级堆 / 内存大 | ZGC | 受限(不建议 .NET)
重点 :.NET 项目主要在 Workstation GC vs Server GC 之间选;Java 选型更复杂(G1 / ZGC / Shenandoah)。
业务对照:
- 设备接入服务(IoT):堆 8G → G1(默认)
- 车牌识别服务(停车场):堆 8G + 50ms 暂停 → ZGC(延迟敏感)
- 视频录像存储:堆 16G → G1 + 大对象走堆外
- Spring Boot 默认:G1(JDK 9+)
2.6 FAQ
-
Q1:G1 的 Region 大小如何确定?
默认按堆大小自动算(堆 8G → Region 4MB)。手动调
-XX:G1HeapRegionSize=N,必须是 2 的幂且 1MB-32MB。Region 太小碎片多,太大并发回收粒度粗。 -
Q2:ZGC 为什么能做到 < 1ms 暂停?
三点:染色指针(指针级元数据无需额外对象头)+ 读屏障(应用线程自己修复引用)+ 并发整理(与应用并发)。G1 还要 STW 整理老年代,ZGC 全程并发。
-
Q3:CMS 的内存碎片问题如何处理?
CMS 用标记-清除不整理 → 长时间运行后碎片多 → 触发 Full GC(Serial Old 单线程整理)。JDK 9 已弃用 CMS,14 移除,建议直接用 G1 / ZGC。
-
Q4:G1 与 ZGC 选型决策树?
看堆大小:< 32G 用 G1;≥ 32G 看延迟:> 100ms 容忍 G1,< 10ms 必须 ZGC。看 JDK 版本:JDK 11 用 G1 为主,JDK 17+ 推荐 ZGC。
3、GC 调优
3.1 调优指标(吞吐 / 延迟 / 容量)
GC 调优的三维目标:
| 指标 | 含义 | 典型目标 |
|---|---|---|
| 吞吐量 Throughput | 业务时间 / 总时间 = 1 - GC 时间占比 |
99%(即 1% 停顿) |
| 延迟 Latency | 单次 GC 暂停时间 | 99% < 100ms |
| 容量 Capacity | 堆内存大小 | 8G / 16G |
三者的取舍:
- 大堆 → 容量↑、吞吐量↑、但延迟↑
- 小堆 → 容量↓、频率↑、但每次暂停短
- 低延迟优先 → 容量↑(代价是硬件)
.NET 对照:
| 指标 | JVM | .NET CLR |
|---|---|---|
| 吞吐 | GC 时间占比 | % Time in GC 性能计数器 |
| 延迟 | STW 暂停 | Gen 2 暂停(PerfView 监控) |
| 容量 | 堆大小 | <gcServer> + <gcConcurrent> |
| 监控 | jstat / jmap | dotMemory / PerfView / EventSource |
重点 :.NET 通过
dotnet-counters监控 GC 性能指标,PerfView 是微软官方的 GC 分析工具。Java 用 GCEasy.io 上传 GC 日志分析。
3.2 关键 JVM 参数
bash
# === 堆内存(最常用)===
-Xms8g # 初始堆大小(建议与 -Xmx 一致,避免动态扩容)
-Xmx8g # 最大堆大小
# -Xmn2g # ⚠️ 已废弃(JDK 14+):G1 不建议显式设新生代
-XX:MetaspaceSize=512m # 元空间初始
-XX:MaxMetaspaceSize=1g # 元空间最大
# === GC 选择(JDK 17 推荐)===
-XX:+UseG1GC # 使用 G1(JDK 9+ 默认)
-XX:MaxGCPauseMillis=200 # 目标暂停时间
-XX:InitiatingHeapOccupancyPercent=45 # 触发并发标记的堆占用
-XX:G1HeapRegionSize=8m # Region 大小(按需调)
# === ZGC 选项(低延迟)===
-XX:+UseZGC # 启用 ZGC
-XX:+UseLargePages # 大页(性能优化)
# === GC 日志(JDK 9+ 通用日志框架)===
-Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=10,filesize=100m
# === OOM 自动 dump ===
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/heapdump/
重点 :.NET 的 GC 配置通过
runtimeconfig.json或环境变量(.NET 6+),比 JVM 友好 。配置可热修改(.NET 5+)。.NET 9 支持GCHeapHardLimit和GCHeapHardLimitPercent更精细控制。
bash
JAVA_OPTS="
-Xms8g -Xmx8g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/heapdump/
-Xlog:gc*:file=/var/log/gc.log:time,uptime,level,tags:filecount=10,filesize=100m
-XX:+DisableExplicitGC
"
.NET 对照(runtimeconfig.json):
json
{
"configProperties": {
"System.GC.Server": true, // 启用 Server GC
"System.GC.Concurrent": true, // 启用并发 GC
"System.GC.CGen": true, // 分代 GC
"System.GC.HeapHardLimit": 85899345920 // 堆上限 80GB
}
}
重点 :.NET 的 GC 配置通过
runtimeconfig.json而非环境变量,比 JVM 友好。配置可热修改(.NET 5+)。
3.3 G1 调优实战
调优四步法:
- 看 GC 日志 → 定位是 Young GC 频繁还是 Mixed GC 慢
- 调目标 →
MaxGCPauseMillis决定 G1 的回收节奏 - 调 IHOP → 太低频繁启动并发标记,太高可能 Full GC
- 调堆大小 → 实在不够再加
典型场景配方:
| 场景 | 参数 | 效果 |
|---|---|---|
| 延迟敏感(车牌识别) | -XX:MaxGCPauseMillis=50 |
G1 拆更多轮次回收 |
| 大对象多(OTA 固件) | -XX:G1HeapRegionSize=16m |
避免大对象跨 Region |
| 提前触发标记(监控) | -XX:InitiatingHeapOccupancyPercent=30 |
更早启动并发标记 |
| 避免 Full GC | -XX:MaxTenuringThreshold=10 |
对象提前晋升 |
| 减少 GC 频率 | -Xmx16g(8G → 16G) |
堆大 → 频率低 |
Mixed GC 调优:
bash
-XX:G1MixedGCCountTarget=8 # 一轮标记后分几次 Mixed GC
-XX:G1MixedGCLiveThresholdPercent=85 # Region 存活对象占比超过 85% 不回收
-XX:InitiatingHeapOccupancyPercent=45 # IHOP 默认 45
.NET 对照(GC 调优 4 步):
- 看 dotnet-counters → 监控
% Time in GC和各代大小 - 调 Server GC →
<gcServer>true</gcServer>(ASP.NET Core 默认) - 调堆大小 → 容器内存限制(
DOTNET_gcHeapHardLimit) - 调 LOH → 用
ArrayPool<byte>池化大对象
重点 :.NET 调优比 Java 简单------大多数情况保持默认 Server GC + Concurrent 即可。Java G1 调优需要根据业务场景精细调整。
3.4 调优方法论
#mermaid-svg-zmSEmFZCp3EQWLMY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zmSEmFZCp3EQWLMY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zmSEmFZCp3EQWLMY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zmSEmFZCp3EQWLMY .error-icon{fill:#552222;}#mermaid-svg-zmSEmFZCp3EQWLMY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zmSEmFZCp3EQWLMY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zmSEmFZCp3EQWLMY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zmSEmFZCp3EQWLMY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zmSEmFZCp3EQWLMY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zmSEmFZCp3EQWLMY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zmSEmFZCp3EQWLMY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zmSEmFZCp3EQWLMY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zmSEmFZCp3EQWLMY .marker.cross{stroke:#333333;}#mermaid-svg-zmSEmFZCp3EQWLMY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zmSEmFZCp3EQWLMY p{margin:0;}#mermaid-svg-zmSEmFZCp3EQWLMY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zmSEmFZCp3EQWLMY .cluster-label text{fill:#333;}#mermaid-svg-zmSEmFZCp3EQWLMY .cluster-label span{color:#333;}#mermaid-svg-zmSEmFZCp3EQWLMY .cluster-label span p{background-color:transparent;}#mermaid-svg-zmSEmFZCp3EQWLMY .label text,#mermaid-svg-zmSEmFZCp3EQWLMY span{fill:#333;color:#333;}#mermaid-svg-zmSEmFZCp3EQWLMY .node rect,#mermaid-svg-zmSEmFZCp3EQWLMY .node circle,#mermaid-svg-zmSEmFZCp3EQWLMY .node ellipse,#mermaid-svg-zmSEmFZCp3EQWLMY .node polygon,#mermaid-svg-zmSEmFZCp3EQWLMY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zmSEmFZCp3EQWLMY .rough-node .label text,#mermaid-svg-zmSEmFZCp3EQWLMY .node .label text,#mermaid-svg-zmSEmFZCp3EQWLMY .image-shape .label,#mermaid-svg-zmSEmFZCp3EQWLMY .icon-shape .label{text-anchor:middle;}#mermaid-svg-zmSEmFZCp3EQWLMY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zmSEmFZCp3EQWLMY .rough-node .label,#mermaid-svg-zmSEmFZCp3EQWLMY .node .label,#mermaid-svg-zmSEmFZCp3EQWLMY .image-shape .label,#mermaid-svg-zmSEmFZCp3EQWLMY .icon-shape .label{text-align:center;}#mermaid-svg-zmSEmFZCp3EQWLMY .node.clickable{cursor:pointer;}#mermaid-svg-zmSEmFZCp3EQWLMY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zmSEmFZCp3EQWLMY .arrowheadPath{fill:#333333;}#mermaid-svg-zmSEmFZCp3EQWLMY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zmSEmFZCp3EQWLMY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zmSEmFZCp3EQWLMY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zmSEmFZCp3EQWLMY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zmSEmFZCp3EQWLMY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zmSEmFZCp3EQWLMY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zmSEmFZCp3EQWLMY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zmSEmFZCp3EQWLMY .cluster text{fill:#333;}#mermaid-svg-zmSEmFZCp3EQWLMY .cluster span{color:#333;}#mermaid-svg-zmSEmFZCp3EQWLMY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zmSEmFZCp3EQWLMY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zmSEmFZCp3EQWLMY rect.text{fill:none;stroke-width:0;}#mermaid-svg-zmSEmFZCp3EQWLMY .icon-shape,#mermaid-svg-zmSEmFZCp3EQWLMY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zmSEmFZCp3EQWLMY .icon-shape p,#mermaid-svg-zmSEmFZCp3EQWLMY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zmSEmFZCp3EQWLMY .icon-shape .label rect,#mermaid-svg-zmSEmFZCp3EQWLMY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zmSEmFZCp3EQWLMY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zmSEmFZCp3EQWLMY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zmSEmFZCp3EQWLMY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} GC 调优
- 收集数据
GC 日志
jstat -gcutil
Arthas dashboard - 设定目标
吞吐 99%
暂停 200ms
堆 16G - 调优
GC 选型
堆大小
暂停目标 - 验证
压测
监控告警
回归测试
.NET 对照(调优流程):
JVM 流程 | .NET CLR 流程
1. 业务指标 | 1. dotnet-counters
2. GC 现状 | 2. PerfView 抓 dump
3. 调参数 | 3. 改 runtimeconfig.json
4. 回归 | 4. dotnet-trace / dotnet-dump
重点 :.NET 工具链更集成(
dotnet-counters/dotnet-trace/dotnet-dump官方),Java 工具更多样(jstat / jmap / jstack / Arthas / async-profiler)。
调优顺序(不能乱):
- 先 业务指标(QPS / P99 延迟) → 知道调优目标
- 再 GC 现状(jstat / 日志) → 知道现状
- 再 调参数 → 一次只改一个变量
- 最后 回归 → 验证
调优红线(不能越界):
- 堆大小不超过物理内存的 70%(预留 OS / 缓存)
- GC 日志保留至少 7 天(滚动)
- 任何 GC 参数调整必须 有监控数据支撑(不要拍脑袋)
3.5 FAQ
-
Q1:JVM 调优目标优先级(吞吐 vs 延迟 vs 容量)?
一般业务:吞吐 > 延迟 > 容量 (吞吐直接影响收入)。金融 / 实时:延迟 > 吞吐 > 容量 。IoT 设备接入:容量 > 吞吐 > 延迟(设备量大优先保稳定)。
-
Q2:-Xms 和 -Xmx 为什么要设置成一样?
避免堆动态扩容。扩容时需 Full GC(容量复制),导致不可控 STW。生产环境全部设置相同,常见 8G / 16G。K8s 部署时配合
requests == limits。 -
Q3:G1 的 IHOP 是什么?如何调?
Initiating Heap Occupancy Percent ------ 触发并发标记的堆占用阈值,默认 45%。调低(如 30)→ 更早开始标记 → 更少 Mixed GC 堆积;调高(如 60)→ 减少并发标记开销但风险 Full GC。
-
Q4:GC 日志分析的关键指标?
Young GC 频率 / 平均暂停 / Mixed GC 次数 / Full GC 次数 / 老年代增长率 / 晋升速率。GCEasy.io 可上传日志自动分析(免费)。
4、线上问题排查
4.1 排查工具矩阵
| 工具 | 用途 | 命令示例 | 适用阶段 |
|---|---|---|---|
| jstat | GC 统计 | jstat -gcutil <pid> 1s |
实时监控 |
| jmap | 堆内存 dump | jmap -heap <pid> / jmap -dump:live,file=heap.hprof <pid> |
OOM 排查 |
| jstack | 线程栈 | jstack <pid> / jstack -l <pid> |
死锁 / 阻塞 |
| jcmd | 综合诊断 | jcmd <pid> GC.heap_info / jcmd <pid> Thread.print |
JDK 7+ 推荐 |
| Arthas | 在线诊断 | dashboard / thread / trace / watch |
阿里开源 |
| JProfiler | 商业分析 | 实时内存 / 线程 / CPU | 桌面工具 |
| async-profiler | 火焰图 | -e cpu -d 30 -f flame.html <pid> |
性能热点 |
| GCEasy.io | 在线分析 | 上传 GC 日志自动分析 | 调优报告 |
| dotnet-dump | 堆 dump | dotnet-dump collect -p <pid> |
OOM 排查 |
| dotnet-trace | 性能跟踪 | dotnet-trace collect -p <pid> |
性能热点 |
| PerfView | 综合分析 | PerfView.exe collect |
GC 调优 |
| dotnet-counters | 实时监控 | dotnet-counters monitor -p <pid> |
实时指标 |
.NET 对照:
重点 :.NET 提供 4 个 dotnet-* 工具,统一命令行;Java 用 jstat / jmap / jstack / Arthas / MAT 各有差异。
JDK 11+ 推荐用 jcmd 替代 jstack / jmap:
bash
# 查看堆信息
jcmd <pid> GC.heap_info
# 查看线程栈
jcmd <pid> Thread.print
# 触发堆 dump
jcmd <pid> GC.heap_dump /tmp/heap.hprof
# 查看 JVM 启动参数
jcmd <pid> VM.flags
4.2 典型 GC 问题 5 种
故障 1:Full GC 频繁
- 现象:Full GC 每分钟 1 次,每次 2 秒(应用明显卡顿)
- 排查 :
jstat -gcutil <pid> 1s看老年代使用率(O 列接近 100%)- GC 日志看 Full GC 触发原因(
Ergonomics/Metadata GC Threshold/Allocation Failure)
- 解决 :
- 调大堆:8G → 16G
- 排查内存泄漏(heap dump + MAT)
- 关掉
DisableExplicitGC检查(防止 System.gc)
.NET 对照(Full GC 频繁):
- .NET 现象 :
% Time in GC> 30% 持续 - .NET 排查 :
dotnet-counters看 Gen 2 回收频率 - .NET 解决 :
- 检查 LOH 分配(> 85000 字节)
- 用
GC.TryStartNoGCRegion临时避免 GC - 升级到 .NET 9 启用 DATAS(区域化 GC)
故障 2:GC 暂停时间过长
- 现象:G1 STW 超过 1 秒
- 排查 :G1 日志中 mixed GC 阶段,看
pause时间分布 - 解决 :
- 调小
MaxGCPauseMillis(200 → 50) - 改用 ZGC(暂停 < 1ms)
- 检查是否有大对象(
-XX:G1HeapRegionSize=16m)
- 调小
故障 3:OOM(OutOfMemoryError)
- 现象 :应用挂了,
java.lang.OutOfMemoryError: Java heap space - 排查 :
jmap -dump:live,file=heap.hprof <pid>(自动 OOM dump 配置-XX:+HeapDumpOnOutOfMemoryError)- MAT 分析大对象 / 支配树 / 重复类
- 解决 :
- 找大对象:MAT 打开 hprof → Leak Suspects → Biggest Objects
- 修复内存泄漏:未关闭的连接 / 静态集合未清理 / ThreadLocal 未 remove
- 临时方案:调大堆(不是根本解决)
故障 4:MetaSpace OOM
- 现象 :
java.lang.OutOfMemoryError: Metaspace - 排查 :
- 类加载器泄漏(自定义 ClassLoader 未释放)
- 动态生成类过多(CGLIB / Groovy / 反射)
- 第三方库过度反射(ORM 字节码增强)
- 解决 :
- 调大 MetaSpace:
-XX:MaxMetaspaceSize=2g - 周期性清理自定义 ClassLoader
- 用 GraalVM 替代 Groovy 动态编译
- 调大 MetaSpace:
故障 5:GC 后内存不释放
- 现象:GC 后堆占用依然 80%+
- 排查:堆 dump → MAT 看支配树(Dominator Tree)
- 解决 :
- 排查大对象 / 缓存淘汰(Caffeine
expireAfterWrite) - 检查 WeakHashMap / ThreadLocal 是否正常清理
- 用 jcmd
VM.native_memory看堆外内存
- 排查大对象 / 缓存淘汰(Caffeine
4.3 排查流程图
#mermaid-svg-u3Zng8ggvWQUUZJb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-u3Zng8ggvWQUUZJb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-u3Zng8ggvWQUUZJb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-u3Zng8ggvWQUUZJb .error-icon{fill:#552222;}#mermaid-svg-u3Zng8ggvWQUUZJb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-u3Zng8ggvWQUUZJb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-u3Zng8ggvWQUUZJb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-u3Zng8ggvWQUUZJb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-u3Zng8ggvWQUUZJb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-u3Zng8ggvWQUUZJb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-u3Zng8ggvWQUUZJb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-u3Zng8ggvWQUUZJb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-u3Zng8ggvWQUUZJb .marker.cross{stroke:#333333;}#mermaid-svg-u3Zng8ggvWQUUZJb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-u3Zng8ggvWQUUZJb p{margin:0;}#mermaid-svg-u3Zng8ggvWQUUZJb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-u3Zng8ggvWQUUZJb .cluster-label text{fill:#333;}#mermaid-svg-u3Zng8ggvWQUUZJb .cluster-label span{color:#333;}#mermaid-svg-u3Zng8ggvWQUUZJb .cluster-label span p{background-color:transparent;}#mermaid-svg-u3Zng8ggvWQUUZJb .label text,#mermaid-svg-u3Zng8ggvWQUUZJb span{fill:#333;color:#333;}#mermaid-svg-u3Zng8ggvWQUUZJb .node rect,#mermaid-svg-u3Zng8ggvWQUUZJb .node circle,#mermaid-svg-u3Zng8ggvWQUUZJb .node ellipse,#mermaid-svg-u3Zng8ggvWQUUZJb .node polygon,#mermaid-svg-u3Zng8ggvWQUUZJb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-u3Zng8ggvWQUUZJb .rough-node .label text,#mermaid-svg-u3Zng8ggvWQUUZJb .node .label text,#mermaid-svg-u3Zng8ggvWQUUZJb .image-shape .label,#mermaid-svg-u3Zng8ggvWQUUZJb .icon-shape .label{text-anchor:middle;}#mermaid-svg-u3Zng8ggvWQUUZJb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-u3Zng8ggvWQUUZJb .rough-node .label,#mermaid-svg-u3Zng8ggvWQUUZJb .node .label,#mermaid-svg-u3Zng8ggvWQUUZJb .image-shape .label,#mermaid-svg-u3Zng8ggvWQUUZJb .icon-shape .label{text-align:center;}#mermaid-svg-u3Zng8ggvWQUUZJb .node.clickable{cursor:pointer;}#mermaid-svg-u3Zng8ggvWQUUZJb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-u3Zng8ggvWQUUZJb .arrowheadPath{fill:#333333;}#mermaid-svg-u3Zng8ggvWQUUZJb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-u3Zng8ggvWQUUZJb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-u3Zng8ggvWQUUZJb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-u3Zng8ggvWQUUZJb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-u3Zng8ggvWQUUZJb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-u3Zng8ggvWQUUZJb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-u3Zng8ggvWQUUZJb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-u3Zng8ggvWQUUZJb .cluster text{fill:#333;}#mermaid-svg-u3Zng8ggvWQUUZJb .cluster span{color:#333;}#mermaid-svg-u3Zng8ggvWQUUZJb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-u3Zng8ggvWQUUZJb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-u3Zng8ggvWQUUZJb rect.text{fill:none;stroke-width:0;}#mermaid-svg-u3Zng8ggvWQUUZJb .icon-shape,#mermaid-svg-u3Zng8ggvWQUUZJb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-u3Zng8ggvWQUUZJb .icon-shape p,#mermaid-svg-u3Zng8ggvWQUUZJb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-u3Zng8ggvWQUUZJb .icon-shape .label rect,#mermaid-svg-u3Zng8ggvWQUUZJb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-u3Zng8ggvWQUUZJb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-u3Zng8ggvWQUUZJb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-u3Zng8ggvWQUUZJb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 慢
挂
抖
GC 问题
应用症状
查 STW 时间
查 OOM
查 GC 频率
GC 日志
heap dump
jstat
调 MaxGCPauseMillis
MAT 分析大对象
调堆 / 调频率
验证
.NET 对照:
JVM 流程 | .NET 流程
查 STW 时间 | dotnet-counters 看 % Time in GC
查 OOM | dotnet-dump collect
查 GC 频率 | dotnet-trace 抓 GC 事件
调 MaxGCPauseMillis | 改 runtimeconfig.json
MAT 分析 | PerfView 分析
重点:.NET 排查流程更标准化(统一 dotnet-* 工具),Java 需要熟悉多种工具(jstat / jmap / jstack / Arthas / MAT)。
黄金排查 SOP:
- 应用报警(Prometheus 看 P99 延迟)
jstat -gcutil看 GC 频率 + 老年代占用- 拉 GC 日志分析(
grep 'Full GC' gc.log) - 必要时 heap dump + MAT
- 调整参数 / 修代码
- 回归压测
4.4 FAQ
-
Q1:如何排查 OOM?
配置自动 dump(
-XX:+HeapDumpOnOutOfMemoryError)→ 拉 hprof → MAT 看 Leak Suspects + Dominator Tree → 找到大对象或泄漏点 → 修代码或加缓存淘汰。常见泄漏源:未关闭连接 / 静态 Map / ThreadLocal / 监听器未解注册。 -
Q2:jstat 输出中 O 列老年代接近 100% 怎么办?
O 列接近 100% 不一定是 Full GC 前兆------可能正常(Mixed GC 会回收)。要看 Full GC 频率和暂停时间。如果 GC 频繁 + 暂停长 → 排查内存泄漏或调大堆。
-
Q3:G1 的并发标记失败(Concurrent Mode Failure)是什么?
并发标记时老年代被填满 → G1 放弃并发标记 → 退化为 Full GC(单线程)。根因 :IHOP 太高或分配速度太快。预防:调低 IHOP 到 30、调大堆、排查大对象。
-
Q4:如何分析 heap dump 文件?
用 MAT(Eclipse Memory Analyzer)或 VisualVM。关键操作:Leak Suspects(自动报告泄漏点)、Dominator Tree(看谁持有最多内存)、Histogram(按类看实例数)。3GB 堆 dump 需要 MAT 8G+ 内存打开。