一、如何判断对象已死

引用计数法

对象被引用时计数器加1,引用失效时减1,计数器为0表示可回收。

缺点:无法解决循环引用问题。

可达性分析

从GC Roots开始搜索,搜索不到的对象即为可回收对象。

GC Roots包括:

  • 虚拟机栈中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI引用的对象

二、垃圾收集算法

标记-清除算法

分为标记和清除两个阶段。

缺点:

  • 效率问题:标记和清除效率都不高
  • 空间问题:产生大量不连续的内存碎片

复制算法

将内存分为两块,每次使用一块,用完后将存活对象复制到另一块。

优点:实现简单,运行高效。
缺点:内存利用率低。

改进:新生代中使用Eden:Survivor比例分配。

标记-整理算法

标记后将存活对象向一端移动,然后清理端边界以外的内存。

优点:不会产生内存碎片。
缺点:移动对象成本较高。

分代收集算法

根据对象存活周期将内存划分为不同区域,采用不同算法。

新生代:复制算法
老年代:标记-清除或标记-整理算法

三、垃圾收集器

Serial收集器

单线程收集器,进行垃圾收集时必须暂停其他线程。

特点:

  • 简单高效
  • 没有线程交互开销
  • 适合客户端模式

ParNew收集器

Serial的多线程版本,使用多个线程进行垃圾收集。

特点:

  • 多线程收集
  • 是Server模式首选的新生代收集器
  • 可与CMS配合

Parallel Scavenge收集器

新生代收集器,关注点是吞吐量。

特点:

  • 吞吐量优先
  • 自适应调节策略
  • 适合后台运算场景

Serial Old收集器

Serial的老年代版本,使用标记-整理算法。

用途:

  • Client模式使用
  • Server模式作为CMS后备方案

Parallel Old收集器

Parallel Scavenge的老年代版本,使用标记-整理算法。

特点:

  • 关注吞吐量
  • 配合Parallel Scavenge使用

CMS收集器

以获取最短回收停顿时间为目标的收集器。

算法:标记-清除

流程:

  1. 初始标记:标记GC Roots直接关联的对象
  2. 并发标记:进行GC Roots Tracing
  3. 重新标记:修正并发标记期间的变动
  4. 并发清除:清除标记的对象

缺点:

  • 对CPU资源敏感
  • 无法处理浮动垃圾
  • 产生内存碎片

G1收集器

面向服务端的垃圾收集器。

特点:

  • 基于Region的内存布局
  • 可预测停顿时间
  • 不产生内存碎片

模式:

  • Young GC
  • Mixed GC
  • Full GC

四、GC日志分析

关键参数:

  • -XX:+PrintGC:打印GC日志
  • -XX:+PrintGCDetails:打印GC详细信息
  • -XX:+PrintGCTimeStamps:打印GC时间戳

日志解读:

  • GC:Minor GC
  • Full GC:Major GC + Minor GC

五、垃圾收集器选择

客户端应用:Serial + Serial Old

服务端应用:

  • 关注吞吐量:Parallel Scavenge + Parallel Old
  • 关注停顿时间:ParNew + CMS 或 G1
  • 大内存应用:G1

六、总结

垃圾收集核心概念:

  • 可达性分析判断对象存活
  • 分代收集提高效率
  • 不同收集器有不同适用场景

选择合适的垃圾收集器需要根据应用特点权衡吞吐量和停顿时间。