JVM(十三)分代收集、增量收集以及分区算法

发布时间 2023-07-12 10:43:23作者: Tod4

JVM(十三)分代收集、增量收集以及分区算法


1 分代收集算法

​ 前面的所有算法中,没有一种算法能够完全替代其他算法,它们都有自己独特的优势和特点,分代收集算法应运而生:

  • 分代收集算法对不同生命周期的对象采取不同的收集方式,一般划分为新生代和老年代,以便提高回收效率

    在Java程序运行的过程中,会产生大量的对象,其中有一些对象是与业务信息相关,比如HTTP请求中的Session对象、线程、Socket连接,这类对象与业务直接挂钩,因此生命周期较长

    而还有一些对象,主要是程序运行过程中产生的临时变量,这类对象的生命周期较短,比如String对象,由于不可变性系统会产生大量的这些对象,有些甚至只用一次即可回收

  • 目前几乎所有的GC垃圾回收器都是采用分代收集算法(Generational Collection)执行垃圾回收的:

    • 年轻代区域相对老年代小,对象生命周期短、存活率低(朝生夕死);而复制算法的效率只和当前存活对象的大小有关,因此年轻代采用复制算法进行垃圾回收,复制算法内存利用率不高的问题,通过两个幸存者区的设计得到了缓解

      默认情况下,新生代和老年代的大小比例为1:2,而新生代中,伊甸园区:幸存者0区:幸存者1区为1:1:8,因此复制算法“浪费”的空间大小仅占内存的1/30,通过这样的设计得到了缓解

    • 老年代区域较大,对象的生命周期长、存活率高,回收不及年轻代频繁,这种情况存在大量存活率高的对象,复制算法明显变得不合适,一般由标记-清除或者标记-清除标记-压缩算法的混合实现

      • Mark标记阶段的开销与存活对象的数量成正比
      • Sweep清除阶段的开销与所管理区域的大小成正比
      • Compact压缩整理阶段的开销与存活对象的数据成正比

      以HotSpot的CMS收集器为例,CMS是基于Mark-Sweep实现的,对于对象的回收效率很高,对于碎片问题,CMS采用基于Mark-Compact算法的Serial Old回收器作为补偿措施:当内存回收不佳(内存碎片问题导致Concurrent Mode Failure)的时候,将采用Serial Old执行Full GC以达到对老年代内存的整理

2 增量收集算法

​ 上面的现有算法,在垃圾回收的过程中,会使应用软件处于一种Stop The World的状态,所有的线程都会被挂起,暂停一切正常工作等待垃圾回收的完成,时间一长就会严重影响用户体验和系统的稳定性增量收集算法则解决了这个问题:

  • 增量收集算法让垃圾收集线程和应用程序线程交替执行,每次垃圾收集线程都只收集一小片区域的内存空间,接着切换为应用程序线程,依次反复直到垃圾收集完成

  • 总的来说,增量收集算法的基础仍是传统的标记-清除复制算法增量收集算法对线程间的妥善处理,允许垃圾收集线程以分阶段的方式完成标记、清理或复制工作

  • 优点:使用这种方式,在垃圾回收过程中,间歇性地还执行了应用程序代码,所以能够减少系统的停顿时间。

  • 缺点:因为线程的切换和上下文的消耗,会使得垃圾回收的成本上升,造成系统吞吐量的下降

3 分区算法

image-20230709162041766
  • 一般来说,在相同条件下,堆空间越大一次GC所需要的时间就越长,有关GC的停顿也就越长。为了更好地控制GC产生的停顿时间,将一块大的内存区域分割成多个小块,根据目标的停顿时间(分配给垃圾回收线程的时间),每次合理地回收若干个小区间,而不是整个堆空间,从而减少一次GC所产生的停顿
  • 分代收集算法按照对象的生命周期长短划分成两个部分,分区算法则将整个堆空间划分成连续的不同小区间
  • 每一个小区间都独立使用,独立回收,这种算法的好处就是可以控制一次回收多少个小区间