Skip to content

垃圾回收算法

2024.1.1 突然想到的

今天下午洗碗的时候,发现我洗碗的方式和垃圾回收的算法好像啊! 我的洗碗步骤如下:

  1. 将所有的碗移到一个水池A中 (共有2个池子且里面都有碗) (只移动)
  2. 把每一个碗上的食物使用清水清洗掉,放到另一个池子B中 —— 轻量级清洗
  3. 将B池子中的每一个碗使用洗洁精清洗后放到A池中 —— 精洗,但这步还没有使用清水冲洗
  4. 将A池中的每一个盘子用清水清洗干净 (清理)
  5. 等都用清水冲洗完之后,碗就清洗干净了,放到厨台上备用 (整理)

有哪些垃圾回收算法?

标记-清除(Mark—Sweep)

被誉为现代垃圾回收算法的思想基础

标记-清除算法采用从根集合进行扫描,对存活的对象标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收。

标记-清除算法不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效,但由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片

复制算法(Copying)

该算法的提出是为了克服句柄的开销和解决堆碎片的垃圾回收。建立在存活对象少,垃圾对象多的前提下。

此算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去后还能进行相应的内存整理,不会出现碎片问题。但缺点也是很明显,就是需要两倍内存空间。

标记-整理

又叫标记-压缩算法(Mark-Compact),又或者叫 标记清除压缩(MarkSweepCompact)

此算法是结合了“标记-清除”和“复制算法”两个算法的优点。避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。

标记-整理算法采用标记-清除算法一样的方式进行对象的标记,但在清除时不同,在回收不存活的对象占用的空间后,会将所有的存活对象往左端空闲空间移动,并更新对应的指针。

标记-整理算法是在标记-清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题

分代回收策略(Generational Collecting)

算法思路:
基于这样的事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的回收算法,以便提高回收效率

实际操作:
新生代由于其对象存活时间短,且需要经常gc,因此采用效率较高的复制算法

老年代和永久代因为其存活对象时间长,因此使用标记清除标记整理算法

JVM常用的垃圾收集器

  1. Serial/Serial Old:单线程垃圾回收器
  2. ParNew:多线程的垃圾回收器(Serial 的多线程版本)
  3. Parallel Scavenge/Parallel Old:吞吐量优先的垃圾回收器 JDK8 默认的垃圾回收器
  4. CMS:最小等待时间优先的垃圾收集器
  5. G1:可控垃圾回收时间的垃圾收集器 JDK 9 之后(HotSpot)默认的垃圾回收器
  6. ZGC:停顿时间超短(不超过 10ms)的情况下尽量提高垃圾回收吞吐量的垃圾收集器 JDK 15 之后默认的垃圾回收器

ZGC

ZGC(Z Garbage Collector)是一种低延迟的垃圾回收器,是 JDK 11 引入的一项垃圾回收技术。它主要针对大内存、多核心的应用场景,旨在减少垃圾回收带来的停顿时间

ZGC核心技术

  1. 并发标记:ZGC 采用增量式并发标记算法来实现并发垃圾回收。它不会在标记阶段产生长时间停顿,可以与用户线程并发运行。
  2. 粉碎压缩:ZGC 采用粉碎压缩算法来避免产生内存碎片。它会将内存按照特定大小(例如 2MB)分为多个区域,然后将存活对象连续放置在相邻区域,释放掉边界外的内存空间。这可以最大限度减少内存碎片。
  3. 直接内存映射:ZGC 会直接映射内存空间,而不需要进行内存分配。这可以避免统计堆内存碎片情况所带来的性能消耗。
  4. 微任务:ZGC 采用了微任务(Microtasks)机制来增量完成垃圾回收工作,从而不会产生长时间停顿。它会将总工作分割为多个微任务,这些微任务会在安全点(Safepoint)之间执行。
  5. 可扩展的堆内存:ZGC 不需要指定最小堆(Xmn)和最大堆(Xmx)大小,它可以跟踪堆内存变化并根据需要动态调整堆空间大小。这使得 ZGC 可以支持将近 4TB 的堆内存。
  6. 可插拔组件:ZGC 是一个独立的 GC 组件,它不依赖于 Gradle 等构建工具,可以与不同的工具或框架一起使用,这增强了其可移植性。

以上技术使ZGC达成毫秒级的停顿和大内存的支持