小言_互联网的博客

当“对象”是垃圾时,如何回收,快学习垃圾收集算法

493人阅读  评论(0)

分代收集理论

目前商业虚拟机的垃圾收集器,大多数都遵循了“分代收集”的理论进行设计,分代收集理论都是建立在弱分代假说(Weak Generational Hypothesis)和强分代假说(Strong Generational Hypothesis)

  • 弱分代假说:绝大多数对象都是朝生夕灭的。
  • 强分代假说:熬过越多次数垃圾收集过程的对象就越难以消亡。
    这两个假说共同奠定了多款常用的垃圾收集器的一致性的设计原则:收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄(对象熬过垃圾回收过程的次数)分配到不同的区域之中存储。
    把分代收集理论具体放到现在的商用Java虚拟机里,设计者一般至少会把Java堆划分为新生代(新生区 Young Generation)和老年代(老年区 Old Generation)两个区域新生代中每次垃圾回收都会有大批对象死去,每次回收后存活的少量对象,将会逐步晋升到老年代中存放。

假如要现在进行一次只局限于新生代区域内的收集(Minor GC),但新生代中的对象是个之外,再额外遍历整个老年代中所有对象来确保可达性分析结果的正确性,反过来也是一样。遍历整个老年代所有对象的方案虽然理论上可行,但无疑会为内存回收带来很大的性能负担。为了解决这个问题,就需要对分代收集理论添加第三条经验法则:

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

这其实是可根据前两条假说逻辑推理得出的隐含推论:存在互相引用关系的两个对象,是应该倾向于同时生存或者同时消亡的。举个例子,如果某个新生代对象存在跨代引用,由于老年代对象难以消亡,该引用会使得新生代对象在收集时同样得以存活,进而在年龄增长之后晋升到老年代中,这时跨代引用也随即被消除了

标记 - 清除算法

标记清除算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象;也可以反过来,标记存活的对象,统一回收未被标记的对象。
缺点

  1. 第一个是执行效率不稳定,
    如果 Java 堆中包含大量对象,而且其中大部分是需要被回收的,这时必须进行大量标记和清除的动作,导致标记和清除两个过程的执行效率都随对象数量增长而降低;
  2. 第二个是内存空间的碎片化问题,标记、清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。如下图所示

标记 - 复制算法

标记一复制算法常被简称为复制算法。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。如果内存中多数对象都是存活的,这种算法将会产生大量的内存间复制的开销,但对于多数对象都是可回收的情况,算法需要复制的就是占少数的存活对象,而且每次都是针对整个半区进行内存回收,分配内存时也就不用考虑有空间碎片的复杂情况,只要移动堆顶指针,按顺序分配即可。这样实现简单,运行高效,不过其缺陷也显而见,这种复制回收算法的代价是将可用内存缩小为原来的一半,造成了空间浪费。如下图所示

标记 - 整理算法

标记整理算法其中的标记过程任然和“标记 - 清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存,如下图所示


转载:https://blog.csdn.net/qq_45039852/article/details/115523091
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场