深入理解JVM - 垃圾收集算法

1. 分代收集理论

当前商业VM的垃圾收集器大多遵循了分代收集 Generational Collection理论进行设计,该理论基于一下分代假说。

  1. 弱分代假说 Weak Generational Hypothesis:绝大多数对象都是朝生夕灭的
  2. 强分代假说 Strong Generational Hypothesis:熬过越多次垃圾收集过程的对象就越难以消亡

两个假说奠定了许多垃圾收集器一致的设计原则:收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。

分代收集理论的经验法则

  • 跨代引用假说 Intergenerational Reference Hypothesis: 跨代引用相对于同代引用来说仅占极少数。

名词

  1. 部分收集 Partial GC:指目标不是完整收集整个Java堆的垃圾收集
  2. 新生代收集 Minor GC/Young GC: 指目标只是新生代的垃圾收集
  3. 年代收集 Major GC/Old GC: 指目标只是老年代的垃圾收集。目前只有CMS收集器会有单独收集老年代的行为。
  4. 混合收集 Mixed GC: 指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收集器会有这种行为
  5. 整堆收集 Full GC: 收集整个Java堆和方法区的垃圾收集

2. 标记-清除算法 Mark-Sweep

先标记存活/死亡的对象,然后进行内存回收。
存在的问题:执行效率不稳定、内存碎片。

3. 标记-复制算法

半区复制Semispace Copying: 可用内存分两块,每次使用其中一块,GC时将存活对象移动到另一块。
问题:内存浪费严重。

Appel式回收: 新生代内存分为较大的 Eden 和 两块较小的 Survivor,每次仅使用 Eden 和一块 Survivor,GC时,将存活对象移动到另一块 Survivor,然后回收 Eden 和 Survivor。一般 Eden : Survivor = 8:1。
当 Survivor 在一次 Minor GC 时不足以存放所有存活对象,,组需要其他内存区域进行分配担保 Handle Promotion

4. 标记-整理 Mark-Compact

标记-复制在对象存活率较大时需要较多复制操作,比较适合新生代。
第一步同样是标记,第二步将存活对象向内存一侧移动,然后清理掉边界外内存。
标记-清除算法与标记-整理算法的本质差异在于前者是一种非移动式的回收算法,而后者是移动式的。

如果移动存活对象,尤其是在老年代这种每次回收都有大量对象存活区域,移动存活对象并更新所有引用这些对象的地方将会是一种极为负重的操作,而且这种对象移动操作必须全程暂停用户应用 程序才能进行,像这样的停顿被最初的虚拟机 设计者形象地描述为Stop The World

但如果像标记-清除那样,内存碎片又会极为严重。

从垃圾收集的停顿时间来看,不移动对象停顿时间会更短,甚至可以不需要停顿,但是从整个程序的吞吐量来看,移动对象会更划算。
HotSpot虚拟机里面关注吞吐量的Parallel Scavenge收集器是基于标记-整理算法的,而关注延迟的CMS收集器则是基于标记-清除算法的。
还有一种“和稀泥式”解决方案可以不在内存分配和访问上增加太大额外负担,做法是让虚拟机平时多数时间都采用标记-清除算法,暂时容忍内存碎片的存在,直到内存空间的碎片化程度已经 大到影响对象分配时,再采用标记-整理算法收集一次,以获得规整的内存空间。前面提到的基于标 记-清除算法的CMS收集器面临空间碎片过多时采用的就是这种处理办法。

posted @ 2022-04-14 22:13  YangDanMua  阅读(26)  评论(0编辑  收藏  举报