【JVM】垃圾收集算法

分代收集理论

当前商业虚拟机的垃圾收集器,大多数都遵循了“分代收集”(Generational Collection)[1]的理论进 行设计,分代收集名为理论,实质是一套符合大多数程序运行实际情况的经验法则,它建立在两个分代假说之上:

  1. 弱分代假说(Weak Generational Hypothesis):绝大多数对象都是朝生夕灭的。

  2. 强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消 亡。

把分代收集理论具体放到现在的商用Java虚拟机里,设计者一般至少会把Java堆划分为新生代 (Young Generation)和老年代(Old Generation)两个区域[2]。顾名思义,在新生代中,每次垃圾收集 时都发现有大批对象死去,而每次回收后存活的少量对象,将会逐步晋升到老年代中存放。

对于分代理论,由于会存在老年代引用新生代的情况,这对于回收对象时的判断会造成性能影响,所以还应该有下面假说

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

上面条结论可根据前两条假说逻辑推理得出的隐含推论:存在互相引用关系的两个对象,是应该倾向于同时生存或者同时消亡的。

下面是一些常见的分代收集名词

  • 部分收集(Partial GC):指目标不是完整收集整个Java堆的垃圾收集,其中又分为:

    • 新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集。

    • 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。目前只有CMS收集器会有单独收集老年代的行为。

    • 混合收集(Mixed GC):指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收集器会有这种行为。

  • 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。

标记-清除算法

标记-清除算法(Mark and Sweep Algorithm)是一种经典的垃圾回收算法,用于识别和释放不再被引用的对象,从而回收内存空间。它包括两个主要阶段:标记(Mark)和清除(Sweep)。

下面是标记-清除算法的工作原理:

  1. 标记(Mark)阶段
    • 从根对象(通常是程序的入口点或全局变量)开始,遍历整个对象图,标记所有可以从根对象访问到的对象。
    • 在这个阶段,被标记的对象通常被打上一个标记位或者加入一个"已标记"的集合中,以表示它们是活跃对象,仍然被引用。
  2. 清除(Sweep)阶段
    • 在清除阶段,垃圾回收器遍历整个堆内存,找到所有未被标记的对象,这些对象是不再被引用的对象。
    • 未被标记的对象被认为是垃圾,垃圾回收器会将它们的内存空间标记为可用空闲空间,以供将来的对象分配使用。
    • 清除后,堆内存中只剩下被标记的对象,而未被标记的对象的内存已经被释放。

上面有一点需要注意的是,标记对象可以是所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象,统一回收所有未被标记的对象。

下面是标记-清除算法的示意图

image-20230904194201368

标记-清除算法的优点是它可以回收不再被引用的对象,但它也有一些缺点:

  • 碎片问题:标记-清除算法会在内存中留下不连续的空闲块,可能导致内存碎片化问题。这会增加分配大对象时的空间不足问题的风险。
  • 效率问题:标记-清除算法需要两次遍历整个堆内存:一次标记,一次清除。这会产生额外的性能开销,尤其是在清除阶段。
  • 停顿问题:标记-清除算法在清除阶段需要停止应用程序的执行,因为它需要整个堆内存处于不变状态才能执行清除操作。这可能导致应用程序在垃圾回收期间出现停顿,影响用户体验。

标记-复制算法

标记-复制算法(Mark and Copy Algorithm)是一种用于垃圾回收的算法,它解决了标记-清除算法中出现的内存碎片问题。标记-复制算法主要用于新生代(Young Generation)的垃圾回收,通常分为两个阶段:标记(Mark)和复制(Copy)。

下面是标记-复制算法的工作原理:

  1. 标记(Mark)阶段

    • 从根对象(通常是程序的入口点或全局变量)开始,遍历整个对象图,标记所有可以从根对象访问到的对象,这些对象被认为是活跃对象。
    • 在这个阶段,被标记的对象通常被打上一个标记位或者加入一个"已标记"的集合中,以表示它们是活跃对象,仍然被引用。
  2. 复制(Copy)阶段

    • 在复制阶段,垃圾回收器会将所有被标记为活跃对象的对象从一个区域(通常称为"From"或"Eden"区)复制到另一个区域(通常称为"To"或"Survivor"区)。
    • 复制后,所有被复制的对象都连续排列在一起,没有碎片,而"From"区变成了空的。
  3. 清理(Clean-Up)阶段

    • 清理阶段不再需要被复制的对象,因此整个"From"区可以被清空,成为新的可用空间,用于将来的对象分配。

示意图如下

image-20230904195708455

标记-复制算法的主要优点是它有效地解决了内存碎片问题。因为被复制的对象都被整齐地排列在一起,"From"区变为空,所以不会出现内存碎片化的情况。这提高了内存的利用率,减少了分配大对象时可能出现的空间不足问题。缺点就是比较浪费空间。

标记-复制算法主要用于新生代的垃圾回收,而老年代通常使用其他算法,如标记-清除或标记-整理算法。这些不同的垃圾回收算法结合在一起,构成了现代Java虚拟机中的复杂垃圾回收策略,以提高内存管理的效率和性能。

标记-整理算法

标记-整理算法(Mark and Compact Algorithm)是一种用于垃圾回收的算法,通常用于老年代(Old Generation)的内存回收。它是标记-清除算法的改进版本,主要解决了标记-清除算法可能导致的内存碎片问题。

相较于标记-清楚算法只是多了一个整理的过程

整理(Compact)阶段

  • 在整理阶段,垃圾回收器会将所有被标记为活跃对象的对象向一端移动,以便它们连续排列在一起。同时,未被标记/被标记的对象都被认为是垃圾,不再需要,它们的内存空间会被释放。
  • 整理后,内存中的活跃对象变得更加紧凑,没有碎片,可以提高内存的利用率。

示意图如下

image-20230904200134998

猜你喜欢

转载自blog.csdn.net/m0_51545690/article/details/132676846
今日推荐