ASP.NET Core迁移Spring系列:运行时与 GC 篇

版本基线

类别 版本
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 的 DirectByteBufferCleaner(虚引用)自动回收,更优雅。

各区域关键参数(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 种触发

  1. 年龄超阈值(默认 15)
  2. Survivor 区相同年龄对象总大小 > Survivor 50% → 直接晋升
  3. 大对象直接进老年代(-XX:PretenureSizeThreshold
  4. 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+ 默认收集器。

核心设计

  1. Region 化:堆拆成最多 2048 个 Region(默认 1MB ~ 32MB,按堆大小自动算)
  2. Garbage-First:优先回收价值最大的 Region(回收空间多 + 回收时间短)
  3. 停顿可控MaxGCPauseMillis 默认 200ms(软目标)
  4. 三色标记 + 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 回收阶段

  1. Young GC:回收所有 Eden + Survivor(STW)
  2. 并发标记:达到 IHOP 阈值(默认 45%)启动
  3. 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 支持 GCHeapHardLimitGCHeapHardLimitPercent 更精细控制。

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 调优实战

调优四步法

  1. 看 GC 日志 → 定位是 Young GC 频繁还是 Mixed GC 慢
  2. 调目标MaxGCPauseMillis 决定 G1 的回收节奏
  3. 调 IHOP → 太低频繁启动并发标记,太高可能 Full GC
  4. 调堆大小 → 实在不够再加

典型场景配方

场景 参数 效果
延迟敏感(车牌识别) -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 步)

  1. 看 dotnet-counters → 监控 % Time in GC 和各代大小
  2. 调 Server GC<gcServer>true</gcServer>ASP.NET Core 默认)
  3. 调堆大小 → 容器内存限制(DOTNET_gcHeapHardLimit
  4. 调 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 调优

  1. 收集数据
    GC 日志
    jstat -gcutil
    Arthas dashboard
  2. 设定目标
    吞吐 99%
    暂停 200ms
    堆 16G
  3. 调优
    GC 选型
    堆大小
    暂停目标
  4. 验证
    压测
    监控告警
    回归测试

.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)。

调优顺序(不能乱):

  1. 业务指标(QPS / P99 延迟) → 知道调优目标
  2. GC 现状(jstat / 日志) → 知道现状
  3. 调参数 → 一次只改一个变量
  4. 最后 回归 → 验证

调优红线(不能越界):

  • 堆大小不超过物理内存的 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 解决
    1. 检查 LOH 分配(> 85000 字节)
    2. GC.TryStartNoGCRegion 临时避免 GC
    3. 升级到 .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 动态编译
故障 5:GC 后内存不释放
  • 现象:GC 后堆占用依然 80%+
  • 排查:堆 dump → MAT 看支配树(Dominator Tree)
  • 解决
    • 排查大对象 / 缓存淘汰(Caffeine expireAfterWrite
    • 检查 WeakHashMap / ThreadLocal 是否正常清理
    • 用 jcmd VM.native_memory 看堆外内存

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

  1. 应用报警(Prometheus 看 P99 延迟)
  2. jstat -gcutil 看 GC 频率 + 老年代占用
  3. 拉 GC 日志分析(grep 'Full GC' gc.log
  4. 必要时 heap dump + MAT
  5. 调整参数 / 修代码
  6. 回归压测

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+ 内存打开。


相关推荐
谁来拯救笨小孩1 小时前
Cursor 流式协议:为何不是 REST,HTTP/2 与双向流怎么选
后端
明月光8181 小时前
在数据库里做权限控制?重新理解 PostgreSQL 的 RLS(行级安全)
后端
用户298698530141 小时前
PDF 转图片?三种方案,覆盖全平台与自动化场景
人工智能·后端
沙湖遇雨1 小时前
2.启动客户端client
后端
程序员只因1 小时前
SpringBoot实战项目目录结构+高频注解详解(黑马点评适配版)
后端
Conan在掘金1 小时前
鸿蒙 7.0 小艺全面进化:超 200 项感知+全天候引擎+超强记忆——AI 私人助理根因
后端
钱六两1 小时前
Spring AI 使用 MCP 客户端(调用高德 MCP)
java·人工智能·spring
老孙讲技术1 小时前
值班室画面慢半拍?5 分钟拿齐乐橙设备 HLS / FLV / 低延迟三条可播路径
后端·物联网
BingoGo2 小时前
PHP 引用计数机制深度解析
后端