文章目录
- 分代收集理论
- 标记-清除算法
- 标记-复制算法
- 标记-整理算法
分代收集理论
当前商业虚拟机的垃圾收集器,大多数都遵循了“分代收集”(Generational Collection)[1]的理论进 行设计,分代收集名为理论,实质是一套符合大多数程序运行实际情况的经验法则,它建立在两个分代假说之上:
-
弱分代假说(Weak Generational Hypothesis):绝大多数对象都是朝生夕灭的。
-
强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消 亡。
把分代收集理论具体放到现在的商用Java虚拟机里,设计者一般至少会把Java堆划分为新生代 (Young Generation)和老年代(Old Generation)两个区域[2]。顾名思义,在新生代中,每次垃圾收集 时都发现有大批对象死去,而每次回收后存活的少量对象,将会逐步晋升到老年代中存放。
对于分代理论,由于会存在老年代引用新生代的情况,这对于回收对象时的判断会造成性能影响,所以还应该有下面假说
- 跨代引用假说(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)。
下面是标记-清除算法的工作原理:
- 标记(Mark)阶段:
- 从根对象(通常是程序的入口点或全局变量)开始,遍历整个对象图,标记所有可以从根对象访问到的对象。
- 在这个阶段,被标记的对象通常被打上一个标记位或者加入一个"已标记"的集合中,以表示它们是活跃对象,仍然被引用。
- 清除(Sweep)阶段:
- 在清除阶段,垃圾回收器遍历整个堆内存,找到所有未被标记的对象,这些对象是不再被引用的对象。
- 未被标记的对象被认为是垃圾,垃圾回收器会将它们的内存空间标记为可用空闲空间,以供将来的对象分配使用。
- 清除后,堆内存中只剩下被标记的对象,而未被标记的对象的内存已经被释放。
上面有一点需要注意的是,标记对象可以是所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象,统一回收所有未被标记的对象。
下面是标记-清除算法的示意图
标记-清除算法的优点是它可以回收不再被引用的对象,但它也有一些缺点:
- 碎片问题:标记-清除算法会在内存中留下不连续的空闲块,可能导致内存碎片化问题。这会增加分配大对象时的空间不足问题的风险。
- 效率问题:标记-清除算法需要两次遍历整个堆内存:一次标记,一次清除。这会产生额外的性能开销,尤其是在清除阶段。
- 停顿问题:标记-清除算法在清除阶段需要停止应用程序的执行,因为它需要整个堆内存处于不变状态才能执行清除操作。这可能导致应用程序在垃圾回收期间出现停顿,影响用户体验。
标记-复制算法
标记-复制算法(Mark and Copy Algorithm)是一种用于垃圾回收的算法,它解决了标记-清除算法中出现的内存碎片问题。标记-复制算法主要用于新生代(Young Generation)的垃圾回收,通常分为两个阶段:标记(Mark)和复制(Copy)。
下面是标记-复制算法的工作原理:
-
标记(Mark)阶段:
- 从根对象(通常是程序的入口点或全局变量)开始,遍历整个对象图,标记所有可以从根对象访问到的对象,这些对象被认为是活跃对象。
- 在这个阶段,被标记的对象通常被打上一个标记位或者加入一个"已标记"的集合中,以表示它们是活跃对象,仍然被引用。
-
复制(Copy)阶段:
- 在复制阶段,垃圾回收器会将所有被标记为活跃对象的对象从一个区域(通常称为"From"或"Eden"区)复制到另一个区域(通常称为"To"或"Survivor"区)。
- 复制后,所有被复制的对象都连续排列在一起,没有碎片,而"From"区变成了空的。
-
清理(Clean-Up)阶段:
- 清理阶段不再需要被复制的对象,因此整个"From"区可以被清空,成为新的可用空间,用于将来的对象分配。
示意图如下
标记-复制算法的主要优点是它有效地解决了内存碎片问题。因为被复制的对象都被整齐地排列在一起,"From"区变为空,所以不会出现内存碎片化的情况。这提高了内存的利用率,减少了分配大对象时可能出现的空间不足问题。缺点就是比较浪费空间。
标记-复制算法主要用于新生代的垃圾回收,而老年代通常使用其他算法,如标记-清除或标记-整理算法。这些不同的垃圾回收算法结合在一起,构成了现代Java虚拟机中的复杂垃圾回收策略,以提高内存管理的效率和性能。
标记-整理算法
标记-整理算法(Mark and Compact Algorithm)是一种用于垃圾回收的算法,通常用于老年代(Old Generation)的内存回收。它是标记-清除算法的改进版本,主要解决了标记-清除算法可能导致的内存碎片问题。
相较于标记-清楚算法只是多了一个整理的过程
整理(Compact)阶段:
- 在整理阶段,垃圾回收器会将所有被标记为活跃对象的对象向一端移动,以便它们连续排列在一起。同时,未被标记/被标记的对象都被认为是垃圾,不再需要,它们的内存空间会被释放。
- 整理后,内存中的活跃对象变得更加紧凑,没有碎片,可以提高内存的利用率。
示意图如下