文章目录
- 1. 垃圾回收算法概述
- 2. 标记-清除算法
- 3. 标记-复制算法
- 4. 标记-整理算法
- 5. 分代回收
本文参考:深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)
1. 垃圾回收算法概述
根据判定对象消亡的角度来看,垃圾收集算法可分为
- 引用计数式垃圾收集(直接垃圾收集)
- 追踪式垃圾收集(间接垃圾收集)
Java虚拟机中采用的是追踪式垃圾收集,追踪式垃圾收集有
- 标记-清除算法
- 标记-复制算法
2. 标记-清除算法
该算法是最基础的收集算法,分给标记和清除两个阶段。
- 标记:标记出所有需要回收的对象
- 清除:在标记完成后,统一回收所有被标记的对象
反过来也可以,标记存活的对象,在标记完成后,清除未被标记的对象。
这里的标记,是根据可达性分析进行标记的,标记的是可达性分析算法中不可达(可达)的对象。
标记-清除算法的缺点
- 执行效率不稳定。如果大部分对象都需要被回收,那么就需要进行大量标记和清除的动作,导致标记和清除两个过程的执行效率都随对象数量增长而降低。
- 内存空间的碎片化问题。标记、清除之后会产生大量不连续的内存碎片,内存碎片会导致需要分配大对象时无法找到连续的内存而不得不触发另一次垃圾收集动作。
3. 标记-复制算法
标记-复制算法是为了解决面对大量可回收对象时的执行效率低的问题。
该算法的思路就是
- 将可用内存按容量划分为大小相等的两块。每次只用其中一块。
- 当这一块内存用完时,就将还存活的对象复制到另一块上面。
- 然后把已经使用过的内存一次清理掉
如果内存中大量对象是存活的,那么这种算法就会产生大量的内存复制的开销。
虽然说这种算法实现起来简单、运行高效,但是也存在缺点
- 将可用内存缩小为原来的一半,空间浪费严重。
而优点却是不会有内存碎片。
4. 标记-整理算法
标记-复制算法在对象存活率高的情况下需要进行较多的复制操作,效率会降低。
并且会浪费一半内存,如果不想浪费内存还需要额外的空间进行分配担保,所以老年代一般不能直接选用这种算法。
标记-整理算法与标记-复制算法不同的是在标记后续的步骤不再是直接对回收对象清除,然后将所有存活对象都向内存空间一端移动,然后清除掉边界外的内存。
移动存活对象,尤其是老年代这种每次回收都有大量对象存活的区域,移动存活对象并更新所有引用这些对象的地址将是一种负重操作,并且在对象移动操作的时候必须全程暂停用户应用程序才能进行,也就是Stop The World
。
5. 分代回收
分代收集理论建立在两个假说之上
- 弱分代假说:绝大多数对象都是朝生夕灭的
- 强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡。
- 跨代引用假说:跨代引用相对于同代引用来说仅占极少数。
- 存在相互引用关系的两个对象,它们应该是同生共死的,由于老年代的对象难以消亡,那么新生代的对象也得以生存,随着年龄增长,新生代对象就会晋升到老年代,跨代引用也就消除了。
Java堆划分为新生代和老年代两个区域。
- 新生代:新生代的绝大多数对象都是朝生夕灭的,每次回收只需要关注如何保存少量存活的对象,而不是去标记那些大量将要回收的对象,就能以较低代价回收到大量空间。
- 老年代:老年代的绝大多数对象为难以消亡的对象,虚拟机便可以使用较低的频率来回收这些区域。
分代回收流程
- 对象首先分配在伊甸园(
Eden Space
) - 新生代空间不足时,触发
minor gc
,伊甸园和from
存活的对象使用copy
复制到to
中,存活的对象年龄加1并且交换from和to
的指向 minor gc
会引发stop the world
,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行 当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit
)- 当老年代空间不足,会先尝试触发
minor gc
,如果之后空间仍不足,那么触发full gc
,STW
的时 间更长
几种收集的定义
- 部分收集(
Partial GC
):指目标不是完整收集整个Java堆的垃圾收集。- 新生代收集(
Minor GC/Young GC
):指目标只是新生代的垃圾收集。 - 老年代收集(
Major GC/Old GC
):指目标只是老年代的垃圾收集。 - 混合收集(
Mixed GC
):指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收集器会有这种行为。
- 新生代收集(
- 整堆收集(
Full GC
):收集整个Java堆和方法去的垃圾收集。