Skip to content

JVM垃圾回收机制深度解析

前言

垃圾回收(Garbage Collection,GC)是Java虚拟机自动内存管理的核心机制。它能够自动识别和回收不再使用的内存对象,避免手动管理内存带来的复杂性和潜在风险。本文将深入探讨JVM垃圾回收的各个方面,包括回收算法、分代策略、STW机制以及现代垃圾回收器使用的三色标记算法。

本文主要针对HotSpot虚拟机进行讲解,不同虚拟机实现可能存在差异。

垃圾回收算法

垃圾回收算法决定了JVM如何识别和清理无用对象。主流的垃圾回收算法包括标记-清除算法、复制算法和标记-整理算法。每种算法都有其独特的优势和适用场景。

标记-清除算法

1765347648170-3380abad-b8a5-4853-ac18-9b52611a26c0.png

标记-清除(Mark-Sweep)算法是最基础的垃圾回收算法,分为两个核心阶段:

标记阶段:从GC Roots出发,遍历整个对象引用图,标记所有可达的存活对象。这里需要注意的是,标记的是需要保留的对象,而不是垃圾对象。

清除阶段:遍历整个堆内存空间,回收所有未被标记的对象所占用的内存。

2eafc374e5c802040e079d91d1a25783.svg

优缺点分析:

优点:

  • 实现简单,逻辑清晰
  • 不需要移动对象,执行速度较快

缺点:

  • 内存碎片化严重:清除后会产生大量不连续的内存碎片。虽然总的空闲内存足够,但可能无法分配较大的连续对象
  • 存储效率降低:碎片过多会导致可用内存利用率下降

举个例子:假设清除后剩余1MB空闲内存,但这些内存分散在各个位置。当需要分配一个1MB的大对象时,虽然空闲内存总量足够,但因为不连续而无法完成分配,只能触发新的GC或抛出内存溢出异常。

复制算法

1765347663118-c98d44d7-ea7b-47cd-aa10-e8d72e71e044.png

为了解决标记-清除算法的碎片化问题,复制算法应运而生。它的核心思想是将内存划分为大小相等的两块区域,每次只使用其中一块。

工作流程:

  1. 对象优先在其中一块内存区域分配
  2. 当这块内存即将用完时,触发垃圾回收
  3. 将存活对象复制到另一块空闲内存区域,复制时按顺序紧密排列
  4. 清空原来使用的内存区域,两块区域角色互换
mermaid
graph TB
    subgraph 回收前
        A1[区域1使用中]
        A2[对象A]
        A3[垃圾B]
        A4[对象C]
        A5[垃圾D]
        A6[对象E]
        A7[区域2空闲]
    end
    
    subgraph 回收后
        B1[区域1空闲]
        B2[区域2使用中]
        B3[对象A]
        B4[对象C]
        B5[对象E]
    end
    
    回收前 --> 回收后
    
    style A1 fill:#4A90E2,stroke:#2E5C8A,stroke-width:2px,rx:10,ry:10
    style A2 fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10
    style A3 fill:#E94B3C,stroke:#B8341F,stroke-width:2px,rx:10,ry:10
    style A4 fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10
    style A5 fill:#E94B3C,stroke:#B8341F,stroke-width:2px,rx:10,ry:10
    style A6 fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10
    style A7 fill:#D3D3D3,stroke:#A9A9A9,stroke-width:2px,rx:10,ry:10
    
    style B1 fill:#D3D3D3,stroke:#A9A9A9,stroke-width:2px,rx:10,ry:10
    style B2 fill:#4A90E2,stroke:#2E5C8A,stroke-width:2px,rx:10,ry:10
    style B3 fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10
    style B4 fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10
    style B5 fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10

优缺点分析:

优点:

  • 内存连续无碎片:存活对象紧密排列,完全避免碎片问题
  • 分配效率高:只需移动空闲指针即可完成对象分配

缺点:

  • 内存利用率低:只能使用一半内存空间,造成严重浪费
  • 复制开销大:当存活对象较多时,复制成本高昂
  • 不适合老年代:老年代对象存活率高,复制操作代价过大

代码示例:

java
/**
 * 电商订单处理系统示例
 * 大量临时订单对象在处理后即可回收,适合使用复制算法
 */
public class OrderProcessor {
    public void processOrders(List<Order> orders) {
        for (Order order : orders) {
            // 创建临时计算对象
            OrderCalculator calculator = new OrderCalculator(order);
            BigDecimal totalAmount = calculator.calculateTotal();
            
            // 生成临时验证对象
            OrderValidator validator = new OrderValidator();
            if (validator.validate(order)) {
                // 处理完成后,calculator和validator成为垃圾对象
                saveOrder(order, totalAmount);
            }
        }
        // 方法结束后大量临时对象可被回收
    }
}

标记-整理算法

1765347674615-432a8185-de9f-4e76-987d-5d626f378d51.png

标记-整理(Mark-Compact)算法结合了前两种算法的优点,特别适合老年代使用。它分为两个阶段:

标记阶段:与标记-清除算法相同,从GC Roots出发标记所有存活对象。

整理阶段:将所有存活对象向内存的一端移动,使它们紧密排列,然后清理边界以外的所有内存。

mermaid
graph TB
    subgraph 标记前
        A[对象1] --> B[对象2]
        C[垃圾3]
        D[对象4] --> E[垃圾5]
        F[对象6]
        G[垃圾7]
    end
    
    subgraph 整理后
        H[对象1] --> I[对象2]
        I --> J[对象4]
        J --> K[对象6]
        L[空闲连续空间]
    end
    
    标记前 --> 整理后
    
    style A fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10
    style B fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10
    style C fill:#E94B3C,stroke:#B8341F,stroke-width:2px,rx:10,ry:10
    style D fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10
    style E fill:#E94B3C,stroke:#B8341F,stroke-width:2px,rx:10,ry:10
    style F fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10
    style G fill:#E94B3C,stroke:#B8341F,stroke-width:2px,rx:10,ry:10
    
    style H fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10
    style I fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10
    style J fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10
    style K fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10
    style L fill:#D3D3D3,stroke:#A9A9A9,stroke-width:2px,rx:10,ry:10

优缺点分析:

优点:

  • 无内存碎片:对象紧密排列,剩余空间完全连续
  • 内存利用率高:不像复制算法浪费一半空间
  • 适合老年代:对象存活率高的场景下表现优异

缺点:

  • 执行效率较低:需要多次遍历和移动对象
  • 更新引用开销:移动对象后需要更新所有指向它的引用

算法性能对比:

从时间效率看:标记-清除 < 复制 < 标记-整理
从空间利用看:标记-整理 > 标记-清除 > 复制
从碎片处理看:标记-整理 ≈ 复制 > 标记-清除

分代回收策略

现代垃圾回收器普遍采用分代回收策略,它基于一个重要的观察:绝大多数对象的生命周期很短,只有少数对象会长期存活

为什么需要分代

根据对象生命周期的不同特征,JVM将堆内存划分为新生代和老年代:

mermaid
graph LR
    A[堆内存] --> B[新生代Young]
    A --> C[老年代Old]
    
    B --> D[Eden区]
    B --> E[Survivor0]
    B --> F[Survivor1]
    
    style A fill:#4A90E2,stroke:#2E5C8A,stroke-width:3px,rx:10,ry:10
    style B fill:#9370DB,stroke:#6A4CB5,stroke-width:2px,rx:10,ry:10
    style C fill:#FF8C69,stroke:#E06B4D,stroke-width:2px,rx:10,ry:10
    style D fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10
    style E fill:#87CEEB,stroke:#5FA8D3,stroke-width:2px,rx:10,ry:10
    style F fill:#87CEEB,stroke:#5FA8D3,stroke-width:2px,rx:10,ry:10

新生代特点:

  • 对象朝生夕死,存活率低(通常小于10%)
  • 垃圾回收频繁
  • 使用复制算法最合适

老年代特点:

  • 对象存活时间长,存活率高
  • 垃圾回收频率低
  • 使用标记-清除或标记-整理算法

新生代的复制算法优化

标准的复制算法会浪费50%内存,新生代通过巧妙的设计优化了这个问题:

  • 将新生代划分为1个Eden区和2个Survivor区(S0和S1)
  • 默认比例为Eden:S0:S1 = 8:1:1
  • 每次只使用Eden区和其中一个Survivor区
  • 回收时将存活对象复制到另一个Survivor区

这样内存利用率从50%提升到90%,大大降低了空间浪费。

代码示例:

java
/**
 * 数据处理管道示例
 * 展示了大量短生命周期对象的处理场景
 */
public class DataPipeline {
    
    public void processStream(Stream<RawData> dataStream) {
        dataStream
            .map(raw -> {
                // 创建临时转换对象,使用后即成为垃圾
                DataTransformer transformer = new DataTransformer();
                return transformer.transform(raw);
            })
            .filter(data -> {
                // 创建临时过滤器对象
                DataFilter filter = new DataFilter();
                return filter.isValid(data);
            })
            .forEach(data -> {
                // 只有通过验证的数据对象会保存到老年代
                persistentStorage.save(data);
            });
        // 处理过程中的大量临时对象在新生代快速回收
    }
}

老年代的算法选择

老年代对象存活率高,选择标记-整理算法可以避免碎片,保证内存连续。但某些场景下,为了降低STW时间,也会使用标记-清除算法(如CMS收集器)。

Stop The World机制

什么是STW

Stop-The-World(STW)是JVM在执行垃圾回收时的一种全局暂停机制。当STW发生时,所有应用线程都会被暂停,只有垃圾回收线程在工作。

mermaid
sequenceDiagram
    participant App as 应用线程
    participant GC as GC线程
    participant JVM as JVM
    
    App->>JVM: 正常运行
    Note over App: 处理业务请求
    
    JVM->>App: 发起STW
    App->>App: 暂停所有线程
    
    JVM->>GC: 启动垃圾回收
    Note over GC: 标记存活对象
    Note over GC: 清理垃圾对象
    
    GC->>JVM: GC完成
    JVM->>App: 恢复运行
    
    Note over App: 继续处理业务

为什么需要STW

STW的必要性主要有两个原因:

1. 保证垃圾回收的完整性

如果不暂停应用线程,垃圾会持续产生,垃圾回收永远无法完成。就像在一个不断有人制造垃圾的房间里打扫卫生,永远也打扫不完。

2. 防止对象引用关系变化导致的错误

在并发环境下,应用线程可能会不断修改对象引用关系,这会导致两个严重问题:

多标(False Positive):本应被回收的垃圾对象被错误标记为存活对象。

  • 影响:产生浮动垃圾,占用额外内存,但下次GC可以清理
  • 危害程度:较低,可以容忍

漏标(False Negative):本应存活的对象被错误标记为垃圾并回收。

  • 影响:正在使用的对象被误删,导致程序崩溃
  • 危害程度:极高,不可接受

STW的影响

STW会带来以下负面影响:

  • 响应时间增加:用户请求在STW期间无法得到处理,导致延迟增加
  • 吞吐量下降:暂停期间无法处理业务,系统QPS下降
  • 性能不可预测:STW时间不固定,导致性能波动

对于高响应性要求的应用(如在线交易系统、实时游戏),长时间的STW是不可接受的。

如何减少STW影响

1. 选择合适的垃圾回收器

现代垃圾回收器通过并发回收技术大幅降低STW时间:

  • CMS(Concurrent Mark-Sweep):大部分工作与应用线程并发执行
  • G1(Garbage-First):可预测的停顿时间
  • ZGC/Shenandoah:亚毫秒级停顿时间

2. 调优JVM参数

  • 合理设置堆大小(-Xms、-Xmx)
  • 调整新生代和老年代比例
  • 设置期望的最大停顿时间(-XX:MaxGCPauseMillis)

3. 优化应用代码

  • 减少对象创建,复用对象
  • 避免创建大对象
  • 及时释放不再使用的对象引用

三色标记算法

算法背景

传统的可达性分析算法需要在整个标记过程中执行STW,这在大堆内存场景下会导致长时间的应用暂停。三色标记算法的引入,使得垃圾回收的大部分工作可以与应用线程并发执行,大幅降低了STW时间。

现代垃圾回收器如CMS、G1都采用三色标记算法作为核心标记机制。

三色定义

三色标记算法将对象分为三种状态,用不同颜色表示:

mermaid
graph TB
    subgraph 颜色含义
        A[白色对象]
        B[灰色对象]
        C[黑色对象]
    end
    
    A -->|说明| D[未被标记的对象<br/>潜在的垃圾对象]
    B -->|说明| E[已标记但引用未扫描完<br/>正在处理的对象]
    C -->|说明| F[已标记且引用已扫描完<br/>确认存活的对象]
    
    style A fill:#F5F5F5,stroke:#BEBEBE,stroke-width:3px,rx:10,ry:10
    style B fill:#A9A9A9,stroke:#696969,stroke-width:3px,rx:10,ry:10
    style C fill:#2F4F4F,stroke:#1C1C1C,stroke-width:3px,rx:10,ry:10
    style D fill:#FFE4E1,stroke:#FF6B6B,stroke-width:2px,rx:10,ry:10
    style E fill:#E6E6FA,stroke:#9370DB,stroke-width:2px,rx:10,ry:10
    style F fill:#E0FFE0,stroke:#50C878,stroke-width:2px,rx:10,ry:10

白色(White):

  • 对象尚未被垃圾回收器访问
  • GC开始时所有对象都是白色
  • GC结束时仍为白色的对象会被回收

灰色(Gray):

  • 对象已被标记,但其引用的对象还未全部扫描
  • 处于待处理队列中
  • 标记过程中的工作集

黑色(Black):

  • 对象已被标记,且其所有引用对象都已扫描完毕
  • 确认为存活对象
  • 不会再被GC处理

标记过程

三色标记算法将垃圾回收分为三个阶段:

阶段一:初始标记(Initial Mark) - 需要STW

  • 标记GC Roots直接引用的对象为灰色
  • 其余对象保持白色
  • 这个阶段非常短暂
mermaid
graph TB
    subgraph 初始标记
        Root1[GC Root 1]
        Root2[GC Root 2]
        Obj1[对象A 灰色]
        Obj2[对象B 白色]
        Obj3[对象C 灰色]
        Obj4[对象D 白色]
        Obj5[对象E 白色]
        
        Root1 --> Obj1
        Root2 --> Obj3
        Obj1 --> Obj2
        Obj3 --> Obj4
        Obj4 --> Obj5
    end
    
    style Root1 fill:#FF6B6B,stroke:#E94B3C,stroke-width:3px,rx:10,ry:10
    style Root2 fill:#FF6B6B,stroke:#E94B3C,stroke-width:3px,rx:10,ry:10
    style Obj1 fill:#A9A9A9,stroke:#696969,stroke-width:3px,rx:10,ry:10
    style Obj2 fill:#F5F5F5,stroke:#BEBEBE,stroke-width:3px,rx:10,ry:10
    style Obj3 fill:#A9A9A9,stroke:#696969,stroke-width:3px,rx:10,ry:10
    style Obj4 fill:#F5F5F5,stroke:#BEBEBE,stroke-width:3px,rx:10,ry:10
    style Obj5 fill:#F5F5F5,stroke:#BEBEBE,stroke-width:3px,rx:10,ry:10

阶段二:并发标记(Concurrent Mark) - 不需要STW

  • 从灰色对象开始,扫描其引用的对象
  • 将引用的白色对象标记为灰色
  • 当前对象的所有引用扫描完后,将其标记为黑色
  • 重复直到没有灰色对象

这个阶段是最耗时的,但可以与应用线程并发执行,不会造成长时间停顿。

mermaid
graph TB
    subgraph 并发标记过程
        direction TB
        Step1[扫描灰色对象A]
        Step2[对象A引用的B标为灰色]
        Step3[对象A标为黑色]
        Step4[扫描灰色对象C]
        Step5[对象C引用的D标为灰色]
        Step6[对象C标为黑色]
        Step7[继续处理其他灰色对象]
        
        Step1 --> Step2
        Step2 --> Step3
        Step3 --> Step4
        Step4 --> Step5
        Step5 --> Step6
        Step6 --> Step7
    end
    
    style Step1 fill:#E6E6FA,stroke:#9370DB,stroke-width:2px,rx:10,ry:10
    style Step2 fill:#E6E6FA,stroke:#9370DB,stroke-width:2px,rx:10,ry:10
    style Step3 fill:#E0FFE0,stroke:#50C878,stroke-width:2px,rx:10,ry:10
    style Step4 fill:#E6E6FA,stroke:#9370DB,stroke-width:2px,rx:10,ry:10
    style Step5 fill:#E6E6FA,stroke:#9370DB,stroke-width:2px,rx:10,ry:10
    style Step6 fill:#E0FFE0,stroke:#50C878,stroke-width:2px,rx:10,ry:10
    style Step7 fill:#E6E6FA,stroke:#9370DB,stroke-width:2px,rx:10,ry:10

阶段三:重新标记(Remark) - 需要STW

  • 修正并发标记期间引用关系变化导致的误标记
  • 处理写屏障记录的变更对象
  • 确保标记的准确性

这个阶段需要STW,但时间很短。

写屏障技术

在并发标记阶段,应用线程可能会修改对象引用关系。写屏障(Write Barrier)是一种在引用更新时自动触发的机制,用于记录引用变化。

java
/**
 * 写屏障伪代码示例
 * 展示引用更新时如何记录变化
 */
public class WriteBarrierExample {
    
    // 应用代码
    public void updateReference(Node parent, Node newChild) {
        // 实际代码:parent.child = newChild;
        
        // JVM在底层插入写屏障代码
        writeBarrier(parent, parent.child, newChild);
        parent.child = newChild;
    }
    
    // 写屏障逻辑(JVM内部实现)
    private void writeBarrier(Object parent, Object oldRef, Object newRef) {
        if (gcInProgress) {
            // 记录引用变化到特殊队列
            recordReferenceChange(parent, newRef);
        }
    }
}

并发标记中的问题

虽然写屏障可以记录大部分引用变化,但并发标记仍可能出现问题:

多标问题:

  • 现象:垃圾对象被错误标记为存活
  • 原因:并发期间对象失去了所有引用,但已被标记
  • 影响:产生浮动垃圾,下次GC清理即可
  • 危害:较小,可接受

漏标问题:

  • 现象:存活对象被错误标记为垃圾
  • 原因:引用关系变化导致对象未被扫描
  • 影响:存活对象被误删,程序崩溃
  • 危害:严重,必须解决

漏标问题的解决方案

漏标问题的发生需要同时满足两个条件:

  • 至少有一个黑色对象指向了白色对象(新建引用)
  • 所有灰色对象删除了对该白色对象的引用(删除旧引用)

解决漏标的关键是破坏其中一个条件。主流方案有两种:

增量更新(Incremental Update)

核心思想:破坏第一个条件,记录黑色对象新增的引用。

当黑色对象新增对白色对象的引用时:

  • 将这个引用关系记录下来
  • 在重新标记阶段,以这些黑色对象为根重新扫描
  • 确保新引用的白色对象能被标记为存活
mermaid
graph TB
    subgraph 增量更新流程
        A[并发标记中<br/>黑色对象A]
        B[新增引用]
        C[白色对象B]
        D[记录A→B引用]
        E[重新标记阶段]
        F[从A重新扫描]
        G[B标记为灰色<br/>然后黑色]
        
        A --> B
        B --> C
        C --> D
        D --> E
        E --> F
        F --> G
    end
    
    style A fill:#2F4F4F,stroke:#1C1C1C,stroke-width:3px,rx:10,ry:10
    style C fill:#F5F5F5,stroke:#BEBEBE,stroke-width:3px,rx:10,ry:10
    style D fill:#FFE4B5,stroke:#FFA500,stroke-width:2px,rx:10,ry:10
    style F fill:#E6E6FA,stroke:#9370DB,stroke-width:2px,rx:10,ry:10
    style G fill:#E0FFE0,stroke:#50C878,stroke-width:2px,rx:10,ry:10

优点:逻辑简单,只需记录新增引用
缺点:需要重新扫描部分黑色对象,略微增加重新标记时间
使用场景:CMS收集器采用此方案

原始快照(Snapshot At The Beginning, SATB)

核心思想:破坏第二个条件,记录删除引用时的白色对象。

当灰色对象删除对白色对象的引用时:

  • 在删除前将这个白色对象记录下来
  • 在重新标记阶段,以这些白色对象为根重新扫描
  • 确保这些对象及其引用链被正确标记
mermaid
graph TB
    subgraph 原始快照流程
        A[并发标记中<br/>灰色对象A]
        B[准备删除引用]
        C[白色对象B]
        D[删除前记录B]
        E[继续并发标记]
        F[重新标记阶段]
        G[从B重新扫描]
        H[B及其引用链<br/>标记为存活]
        
        A --> B
        B --> C
        C --> D
        D --> E
        E --> F
        F --> G
        G --> H
    end
    
    style A fill:#A9A9A9,stroke:#696969,stroke-width:3px,rx:10,ry:10
    style C fill:#F5F5F5,stroke:#BEBEBE,stroke-width:3px,rx:10,ry:10
    style D fill:#FFE4B5,stroke:#FFA500,stroke-width:2px,rx:10,ry:10
    style G fill:#E6E6FA,stroke:#9370DB,stroke-width:2px,rx:10,ry:10
    style H fill:#E0FFE0,stroke:#50C878,stroke-width:2px,rx:10,ry:10

优点:基于GC开始时的快照,保证一致性
缺点:可能产生浮动垃圾(本应删除的对象被保留)
使用场景:G1收集器采用此方案

两种方案对比

特性增量更新原始快照
破坏条件第一个(记录新增引用)第二个(记录删除引用)
重新标记扫描黑色对象扫描白色对象
浮动垃圾较少较多
实现复杂度较低较高
典型应用CMSG1、Shenandoah

初始标记和重新标记为何需要STW

理解了三色标记算法后,我们来解释为什么初始标记和重新标记需要STW,而并发标记不需要。

初始标记必须STW的原因

初始标记阶段需要扫描GC Roots,而大多数GC Roots并不是普通对象:

  • 虚拟机栈中的局部变量
  • 本地方法栈中的引用
  • 类静态属性
  • 常量池引用
  • 同步锁持有的对象

这些根引用位于特殊的内存区域,写屏障技术无法监控它们的变化。如果不STW,根引用可能在扫描过程中发生变化,导致初始标记结果不准确。

虽然这个阶段需要STW,但扫描速度很快,因为只标记直接引用的对象,不需要遍历整个对象图。

并发标记不需要STW

并发标记阶段遍历整个对象图,这是最耗时的阶段。通过写屏障技术,可以记录并发期间的引用变化,使这个阶段可以与应用线程并发执行,大幅降低停顿时间。

虽然并发标记可能产生一些误标记,但通过重新标记阶段可以修正。

重新标记必须STW的原因

重新标记是垃圾回收的最后确认阶段,它的任务是:

  • 处理并发标记期间记录的所有引用变化
  • 修正可能的漏标记问题
  • 确保标记结果的完全准确

因为这是做最终决策的阶段,必须保证准确性。如果此时不STW,新的引用变化会持续产生,永远无法得到确定的标记结果。

好在并发标记已经完成了大部分工作,重新标记只需要处理增量变化,所以STW时间很短。

mermaid
graph LR
    A[初始标记<br/>STW短暂] --> B[并发标记<br/>不需要STW<br/>最耗时]
    B --> C[重新标记<br/>STW短暂]
    C --> D[并发清除<br/>不需要STW]
    
    style A fill:#FF6B6B,stroke:#E94B3C,stroke-width:2px,rx:10,ry:10
    style B fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10
    style C fill:#FF6B6B,stroke:#E94B3C,stroke-width:2px,rx:10,ry:10
    style D fill:#50C878,stroke:#2E7D4E,stroke-width:2px,rx:10,ry:10

总结

本文深入探讨了JVM垃圾回收机制的核心内容:

  • 垃圾回收算法:标记-清除、复制、标记-整理各有优劣,适用于不同场景
  • 分代回收策略:基于对象生命周期特征,新生代使用复制算法,老年代使用标记-整理算法
  • STW机制:保证垃圾回收的正确性,但会影响应用响应时间,需要通过选择合适的收集器来优化
  • 三色标记算法:实现并发标记,大幅降低STW时间,是现代垃圾回收器的核心技术
  • 漏标问题解决:通过增量更新或原始快照方案,确保并发标记的准确性

理解这些机制对于JVM性能调优、问题排查都至关重要。在实际应用中,应根据业务特点选择合适的垃圾回收器,并通过合理的参数配置来平衡吞吐量和响应时间。

更新: 2025-12-10 14:21:25
原文: https://www.yuque.com/u22210564/zoxfmt/doc-05-jvm-02-04

Java 后端面试知识库