JVM垃圾收集算法

目录

垃圾收集算法

分代收集理论

三个分代假说

不同分代收集名词

标记-清除算法

算法原理

算法缺点

标记-复制算法

算法原理

算法优点

算法缺点

标记-整理算法

算法优点

算法缺点


垃圾收集算法

垃圾收集算法划分为

  •  “引用计数式垃圾收集”(Reference Counting GC)----直接垃圾收集
  •  “追踪式垃圾收集”(Tracing GC)两大类,----间接垃圾收集

分代收集理论

分代收集名为理论,实质是一套符合大多数程序运行实际情况的经验法则。

三个分代假说

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

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

共同点:

        收集器应该将Java堆花费出不同的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。

如果一个区域中大多数对象都是朝生夕灭,难以熬过垃圾收集过程的话,把它们集中放在一起,每次回收时只关注如何保留少量存活而不是去标记那些大量将要被回收的对象,就能以较低代价回收到大量的空间;

如果剩下的都是难以消亡的对象,那把他们集中放在一块,虚拟机便可以使用较低的频率来回收这个区域,这就同时兼顾了垃圾收集的时间开销和内存的空间有效利用。

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

由来:假如要现在进行一次只局限于新生代区域内的收集(Minor GC),但新生代中的对象是完全有可能被老年代所引用的,为了找出该区域中的存活对象,不得不固定的GC Roots之外,再额外遍历整个老年代中所有对象来确保可达性分析结果的正确性,反过来也是一样。

遍历整个老年代所有对象的方案虽然理论上可行,但无疑会为内存回收带来很大的性能负担。 所以才产生了第三条经验法则,跨代引用假说。

不同分代收集名词

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

名词 解释
新生代收集(Minor GC/Young GC) 指目标知识新生代的垃圾收集
老年代收集(Major GC/Old GC)

指目标只是老年代的垃圾收集。

目前只有CMS收集器会有单独收集老年代的行为。

混合收集(Mixed GC)

指目标是收集整个新生代以及部分老年代的垃圾收集。

目前只有G1收集器会有这种行为。

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

标记-清除算法

        最早出现也是最基础的垃圾收集算法是“标记-清除”(Mark-Sweep)算法,在1960年由Lisp之父John McCarthy所提出。

算法原理

  1. 首先标记出所有需要回收的对象,
  2. 在标记完成后,统一回收掉所有被标记的对象,
  3. 也可以反过来,标记存活的对象,统一回收所有未被标记的对象。

算法缺点

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

JVM垃圾收集算法_第1张图片

标记-复制算法

        标记-复制算法简称为复制算法。1969年Fenichel提出了一种称为“半区复制”(Semispace Copying)的垃圾收集算法。

算法原理

  1. 将可用内存按容量划分为大小相等的两块,每次只是用其中的一块。
  2. 当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
  3. 如果内存中多数对象都是存活的,这种算法将会产生大量的内存间复制的开销,但对于多数对象都是回收的情况,算法需要复制的就是占少数的存活对象,而且每次都是针对整个半区进行内存回收,分配内存时也就不用考虑有空间碎片的复杂情况,只要移动堆顶指针,按顺序分配即可。

        JVM垃圾收集算法_第2张图片

算法优点

  • 解决了“标记-清除算法”的执行效率不稳定问题,该算法主要应用于新生代,新生代对象大部分都是朝生夕灭,新生代被划分为Eden、From Survivor、To Survivor,每次使用Eden和一个Survivor用于新生对象的存储。垃圾回收时就把Eden、From Survivor中存活的对象复制到另一个Survivor上,这样便不会有执行效率不稳定的情况,因为大部分对象都是朝生夕灭的(该优点是相对于新生代来说)。
  • 解决了“标记-清除算法”的空间碎片化问题,因为是清空Eden FromSurvivor所以不会再有不连续的空间碎片。

算法缺点

由于JVM中的绝大多数对象都是瞬时状态的,生命周期非常短暂,所以复制算法被广泛应用于年轻代中。

  • 标记-复制算法因为必须有一部分空间时刻空闲着,所以会有一定的空间浪费
  • 正在极端情况下To Survivor区域不一定能存储的了新生代存活下来的对象,所以需要分配担保策略(对象进入老年代)。

标记-整理算法

        标记-复制算法在对象存活率较高时就要进行较多的复制操作,效率将会降低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都是100%存活的极端情况,所以在老年代一般不能直接选用这种算法。

        1974年Edward Lueders提出了另外一种有针对性的“标记-整理”(Mark-Compact)算法,其中的标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。

JVM垃圾收集算法_第3张图片

JVM垃圾收集算法_第4张图片

算法优点

  • 解决了“标记-清除算法”的空间碎片化问题
  • 解决了“标记-复制算法”需要分配担保的问题

算法缺点

        根据强分代假说“熬过越多次垃圾收集的对象,越难以被回收”,老年代中的大部分对象都是年龄达到了16的对象,都是很难被回收的,所以采用“标记-整理算法”去移动对象,对应用程序的吞吐量其实影响很大,但是不得不使用“标记-整理算法”,因为“标记-清除算法”会浪费一定空间,“标记-复制算法”又必须有分配担保策略也需要浪费空间,且“标记-复制算法”也无法满足老年代中所有对象都存活的极端情况。

作者:筱白爱学习!!

欢迎关注转发评论点赞沟通,您的支持是筱白的动力!

你可能感兴趣的:(爱学习→Java,java,算法,jvm)