JVM的垃圾回收
JVM的垃圾回收
一、JVM的垃圾回收算法
垃圾回收机制是JVM的一个重要机制,它可以自动帮助开发者清除已弃用的对象,达到内存回收的目的。而不同的内存需要采用不同的垃圾回收算法,确保兼顾效率和回收率。
常见的垃圾回收算法有以下四种:
- 标记-清除算法 :
- 标记阶段:从一组GC Roots对象出发,遍历所有的可达对象,并标记为“存活”。
- 清除阶段:遍历堆内存,将未标记为存活的对象清除,从而释放内存。
- 复制算法 :
- 将内存分为
From和To两块区域,对象创建在From区域,触发垃圾回收机制时,JVM会将From区域的所有存活对象复制到To区域,并清除From区域的所有空间,最后再调换From和To区域。从而达到内存回收的目的。
- 将内存分为
- 标记-整理算法 :
- 标记阶段:与标记-清除算法相同。
- 整理阶段:将存活的对象移到内存的一端,并清除剩余的空间。
- 分代GC算法 :
将对象分为新生代 和老年代- 新生代:分为Eden区 和Survivor区 (Survivor0和Survivor1)。新对象创建一般在Eden区,当Eden区内存不足时,会触发Minor GC ,在Survivor区采用复制算法回收内存。
- 当对象为大对象/在新生代经历多次GC仍存活时,对象就会存储在老年代。老年代一般采用标记-清除算法或标记-整理算法。
二、如何判断对象是否存活
- 引用计数法 :
机制 :对每个对象的被引用数 进行统计,当有新的引用指向该对象时,引用计数+1;反之-1。如果引用数为0,我们可以初步判断该对象已经死亡。
缺点 :当两个死亡对象出现循环引用时,引用计数不为0,但是仍需要回收。 - 可达性分析法 :
通过一组GC Roots对象遍历所有可达对象,遍历后标记的便为存活对象。- GC Roots对象有:
- JVM中栈的引用(方法的局部变量、参数等)
- 静态变量引用
- 运行时常量池中的引用
- JNI本地方法引用
- 对象引用类型:
- 强引用:new 创建的对象
- 弱引用:在内存不足时会回收,用于缓存
- 软引用:只要触发GC就会回收
- 虚引用:无法通过虚引用访问对象,主要用于跟踪对象的存活状态
- 对象的生存状态:
- 可达:对象能被GC Roots访问
- 可回收(第一次标记):对象不可达,但是可能复活(
finalize()方法中重新引用GC Roots) - 不可复活(第二次标记):
finalize()之后仍不可达,则认定为垃圾
- GC Roots对象有:
三、JVM中的垃圾回收器
常用的垃圾回收器为G1 和CMS 回收器:
- CMS回收器 ,以最小化停顿时间为目标,适用于高响应 的服务(如Web应用),基于标记-清除 算法,清理流程如下:
- 停止所有用户线程(STW),启动一个GC线程 进行初始标记 ,遍历可达对象。
- 用户线程和GC线程进行并发标记 ,分析对象存活情况。
- 停止所有用户线程,启动多个GC线程 进行最终标记 ,修正并发标记过程中产生的变动
- 启动多个用户线程和一个GC线程 ,进行并发清除 ,完成后重置GC线程。
优点 :响应快 ,停顿时间短
缺点 :标记清除算法会产生内存碎片 ;并发标记过程用户线程 产生的垃圾需要下次才能回收
- G1回收器 :以控制GC停顿时间为目标,兼具高吞吐量和低延迟性能,适用于大内存、多核环境。基于标记-整理和标记-复制算法,回收过程如下:
- 停止所有用户线程(STW),启动一个GC线程 进行初始标记,遍历可达对象。
- 用户线程和一个GC线程 进行二次标记 ,分析对象存活情况。
- 停止所有用户线程(STW),启动多个GC线程 进行并发最终标记 ,修正标记过程中产生的引用变动。
- 让多个GC线程进行筛选回收 ,根据收集时间预算,优先回收回收价值最高的区域 ,回收完之后对GC线程进行重置。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Aromatic!



