2024-10-26
版权声明:本文为博主付费文章,严禁任何形式的转载和摘抄,维权必究。 本文链接:https://www.skjava.com/mianshi/baodian/detail/2342453000

回答

新生代 GC 算法使用的是复制算法,且新生代存放的对象都是生命周期比较短的对象,其特点就是每次 GC 后,存活的对象不会很多。

在一次 GC 过程中,JVM 将会:

  • 清理 Eden 区和一个 Survivor 区(例如 S0)
  • 将 Eden 区中存活的对象以及一个 Survivor 区(S0)中存活的对象,复制到另一个 Survivor 区(S1)。

为什么这里需要两个 Survivor 区呢?其核心目的优化 GC 过程中的拷贝过程、减少内存碎片,同时能够更好地支持对象的年龄和晋升。

扩展

我们知道新生代中的对象绝大多数都是朝生夕死,采取的 GC 的算法也是复制算法,复制算法好是好,但是它有一个缺点就是每次只能使用 1/2 的内存空间。如果我们将新生代一分为二,那么内存的利用率就比较低,为了更好地利用内存,我们希望新生代使用的内存大于 1/2。如果我们只将新生代一分为二的话,就无法使新生代的内存空间始终都大于 1/2,这次大,下次肯定小。因为每次 GC 新生代存活的对象比较少,所以我们可以单独为这个存活的对象分配一个空间,但是为了更好地处理对象的复制过程,我们一分为三,即一个 Eden 区,两个 Survivor,默认大小为 8:1:1。每次新生代对象在 Eden 区分配,上一次 GC 存活的对象在 From 区,下次 GC 时要移动的对象是 Eden 区和 From 区中存活的对象,移动至 To 区,然后 From 区和 To 区身份交换。这样我们新生代可使用的内存空间就有 9/10(Eden + From) 了。

同时,由于两个 Survivor 区可以轮流使用,所以每次垃圾回收后,实际的内存碎片会更少。

对象在经过若干次垃圾回收后(默认 15 次),如果仍然存活,就会从新生代晋升到老年代。而两个 Survivor 能够更好地支持对象年林计数和晋升: