Administrator
Administrator
Published on 2025-01-13 / 1 Visits
0
0

2024.12.19

JVM中的垃圾回收算法

标记-清除

标记清除是最简单和干脆的一种垃圾回收算法,他的执行流程是这样子的:当 JVM 识别出内存中的垃圾以后,直接将其清除,但是这样有一个很明显的缺点,就是会导致内存空间的不连续,也就是会产生很多的内存碎片。

  • 优点

  • 速度快,因为不需要移动和复制对象

  • 缺点

  • 会产生内存碎片,造成内存的浪费

标记-复制

复制算法首先将内存划分成两个区域。新创建的对象都放在其中一块内存上面,当快满的时候,就将标记出来的存活的对象复制到另一块内存区域中(注意:这些对象在复制的时候其内存空间上是严格排序且连续的),这样腾出来的那一半就又变成了空闲空间了。依次循环运行。

  • 优点

  • 内存空间是连续的,不会产生内存碎片

  • 缺点

  • 浪费了一半的空间

  • 复制对象会造成性能和时间上的消耗

标记-整理

标记:他的第一阶段与标记-清除算法是一模一样的,均是遍历 GC Roots,然后将存活的对象标记。

整理:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。

  • 优点

  • 不会产生内存碎片

  • 不会浪费内存空间

  • 缺点

  • 太耗时间(性能低)

JVM 中一次完整的 GC 流程

基于 JDK 1.8

java_gc.jpg

一般来说,GC的触发是在对象分配过程中,当一个对象在创建时,他会根据他的大小决定是进入年轻代或者老年代。如果他的大小超过 -XX:PretenureSizeThreshold就会被认为是大对象,直接进入老年代,否则就会在年轻代进行创建。(PretenureSize Threshold 默认是 0,也就是说,默认情况下对象不会提前进入老年代,而是直接在新生代分配。然后 GC 次数和基于动态年龄判断来进入老年代)

在年轻代创建对象,会发生在 Eden 区,但是这个时候有可能会因为 Eden 区内存不够,这时候就会尝试触发一次 YoungGC。(会在 YoungGC 前做一次空间分配担保,如果失败可能直接触发 FullGC)

年轻代采用的是标记复制算法,主要分为:标记、复制、清除三个步骤,会从 GC Root 开始进行存活对象的标记,然后把 Eden 区和 Survivor 区复制到另外一个 Survivor 区。然后再把 Eden 和 From Survivor 区的对象清理掉。

这个过程,可能会发生两件事情,第一个就是 Survivor 有可能存不下这些存活的对象,这时候就会进行空间分配担保。如果担保成功了,那么就没什么事儿,正常进行 Young GC 就行了。但是如果担保失败了,说明老年代可能也不够了,这时候就会触发一次 Full GC 了。第二件事就是,这个过程中,会进行对象的年龄判断,如果他经过一定次数的 GC 之后,还没有被回收,那么这个对象就会被放到老年代当中去。

而老年代如果不够了,或者担保失败了,那么就会触发老年代的 GC,一般来说,现在用的比较多的老年代的垃圾收集器是 CMSG1,他们采用的都是三色标记法

也就是说一共分为四个阶段:初始标记、并发标记、重新标记、及并发清理

老年代在做 Full GC 之后,如果空间还是不够,那就要触发 OOM 了。


Comment