JVM垃圾回收器详解
一、如何判断对象已死
引用计数法
对象被引用时计数器加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收集器
以获取最短回收停顿时间为目标的收集器。
算法:标记-清除
流程:
- 初始标记:标记GC Roots直接关联的对象
- 并发标记:进行GC Roots Tracing
- 重新标记:修正并发标记期间的变动
- 并发清除:清除标记的对象
缺点:
- 对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
六、总结
垃圾收集核心概念:
- 可达性分析判断对象存活
- 分代收集提高效率
- 不同收集器有不同适用场景
选择合适的垃圾收集器需要根据应用特点权衡吞吐量和停顿时间。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 夏天的风吹向哪里!
