图解JVM - 12.垃圾回收相关概念

35 阅读3分钟

1. System.gc()的理解

工作机制图解

核心要点

  1. 非强制调用<font style="background-color:rgb(252, 252, 252);">System.gc()</font>本质是<font style="background-color:rgb(252, 252, 252);">Runtime.getRuntime().gc()</font>的封装,只是建议而非强制
  2. 执行不确定性
    • 可能触发Full GC(YoungGC+OldGC+MetaspaceGC)
    • 具体行为取决于<font style="background-color:rgb(252, 252, 252);">-XX:+ExplicitGCInvokesConcurrent</font>参数
  3. 生产环境禁用建议
-XX:+DisableExplicitGC  # 禁止显式GC调用
-XX:+ExplicitGCInvokesConcurrent  # 使用并发方式执行

2. 内存溢出与内存泄露

内存溢出(OOM)图解

常见OOM类型

  1. Heap OOM<font style="background-color:rgb(252, 252, 252);">Java heap space</font>
  2. Metaspace OOM<font style="background-color:rgb(252, 252, 252);">Metaspace</font>
  3. 栈OOM<font style="background-color:rgb(252, 252, 252);">Requested stack size exceeded</font>
  4. 直接内存OOM<font style="background-color:rgb(252, 252, 252);">Direct buffer memory</font>

内存泄漏(Memory Leak)案例

public class MemoryLeak {
    static List<Object> list = new ArrayList<>();

    public void addData() {
        for(int i=0; i<1000; i++){
            list.add(new byte[1024 * 1024]); // 持续添加大对象
        }
    }
}

泄漏特征

  1. 对象不再使用但仍被GC Roots引用
  2. 常见泄漏场景:
    • 静态集合类
    • 未关闭的资源(数据库连接、文件流)
    • 监听器未注销

3. Stop The World

STW发生机制图解

关键影响与优化

  1. 暂停时间指标
    • CMS:<font style="background-color:rgb(252, 252, 252);">-XX:MaxGCPauseMillis=200</font>(默认200ms)
    • G1:<font style="background-color:rgb(252, 252, 252);">-XX:MaxGCPauseMillis=200</font>
  2. 优化策略
-XX:+UseParallelGC       # 并行回收缩短STW
-XX:+UseConcMarkSweepGC  # CMS并发标记
-XX:+UseG1GC             # G1的可预测暂停

4. 垃圾回收的并行与并发

概念对比图解

技术实现细节

  1. 并行(Parallel)

- <font style="color:rgba(0, 0, 0, 0.9);background-color:rgb(252, 252, 252);">典型场景:</font>`<font style="background-color:rgb(252, 252, 252);">-XX:+UseParallelGC</font>`<font style="color:rgba(0, 0, 0, 0.9);background-color:rgb(252, 252, 252);">(ParNew/Parallel Scavenge)</font>

2. 并发(Concurrent)

- <font style="color:rgba(0, 0, 0, 0.9);background-color:rgb(252, 252, 252);">典型场景:CMS的并发标记阶段</font>

5. 安全点与安全区域

安全点(Safepoint)机制

主动式中断实现

// HotSpot虚拟机源码片段
void SafepointSynchronize::block_jni_calls() {
    while (true) {
        if (SafepointSynchronize::is_synchronized()) break;
        os::naked_short_sleep(1);  // 自旋等待
    }
}

安全区域(Safe Region)


6. 再谈引用:强引用

强引用生命周期

典型代码示例

Object obj = new Object();  // 强引用
obj = null;  // 断开引用后对象可被回收

强引用与内存泄漏

7. 再谈引用:软引用

回收机制图解

典型使用场景

// 创建软引用对象
SoftReference<byte[]> softRef = new SoftReference<>(new byte[10 * 1024 * 1024]);

// 获取对象(可能返回null)
byte[] data = softRef.get(); 
if(data == null) {
    // 重新加载数据
}

参数调优

-XX:SoftRefLRUPolicyMSPerMB=1000  # 每MB空闲内存保留软引用1秒

8. 再谈引用:弱引用

生命周期图解

WeakHashMap应用

WeakHashMap<Key, Value> cache = new WeakHashMap<>();
Key key = new Key();
cache.put(key, new Value());

key = null;  // 下次GC时自动清理条目

9. 再谈引用:虚引用

对象跟踪机制

典型应用场景

// 创建虚引用(必须关联引用队列)
PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);

// 监控对象回收(常用于堆外内存管理)
while(true) {
    Reference<?> ref = queue.remove();
    // 执行内存回收后续操作
}

10. 终结器引用

finalize()执行流程

代码警示案例

public class Zombie {
    static Zombie saved;

    @Override
    protected void finalize() {
        saved = this; // 对象复活
    }
}

11. 常见问题与解决方案

内存泄漏排查工具链

MAT分析步骤

  1. 生成堆转储文件
jmap -dump:format=b,file=heap.bin <pid>
  1. 查看支配树(Dominator Tree)
  2. 分析对象引用链

12. 高频面试问题与解答

问题1:System.gc()一定会触发GC吗?

:不一定。取决于JVM实现和运行参数,使用<font style="background-color:rgb(252, 252, 252);">-XX:+DisableExplicitGC</font>会完全禁用该调用。

问题2:内存泄漏和内存溢出的区别?

  • 内存泄漏:对象不再使用但无法回收(长期积累导致OOM)
  • 内存溢出:当前内存空间无法满足分配需求(可能瞬时发生)

问题3:如何减少STW时间?

  1. 使用G1/CMS等低延迟收集器
  2. 调整堆大小:<font style="background-color:rgb(252, 252, 252);">-Xmx4g -Xms4g</font>
  3. 控制年轻代比例:<font style="background-color:rgb(252, 252, 252);">-XX:NewRatio=2</font>

问题4:弱引用和软引用的核心区别?

  • 软引用:内存不足时回收(适合缓存)
  • 弱引用:发现即回收(适合辅助数据结构)

问题5:安全点如何影响GC性能?

  • 安全点过少 → STW等待时间增加
  • 安全点过多 → 运行时性能损耗
  • 优化方向:<font style="background-color:rgb(252, 252, 252);">-XX:GuaranteedSafepointInterval=30000</font>

总结图谱