为什么 Java 的垃圾收集器将堆分为老年代和新生代?

Sherwin.Wei Lv8

为什么 Java 的垃圾收集器将堆分为老年代和新生代?

回答重点

主要是为了提高垃圾回收效率,依据对象的生命周期特点来进行优化。

对象的生命周期特点

  • 大多数对象存活时间短:大部分对象会很快变成垃圾,不再被使用,这些短生命周期的对象会分配在新生代。
  • 少部分对象存活时间长:一些长期存活的对象不会很快被回收,分配在新生代的对象经过多次垃圾回收仍存活的,将晋升到老年代。

所以按照存活时间分区管理更加高效,也因为不同分区的生命周期不同,所以可以采用不同的清除算法来优化处理

不同的回收算法

  • 新生代的回收:新生代通常采用 复制算法,因为新生代中大部分对象生命周期短,大部分会在一次 GC 中被回收,复制算法只需要在内存中保留少量存活对象,并将它们复制到 Survivor 空间,回收剩余区域。这种算法效率很高,适合新生代对象频繁创建和回收的特点。
  • 老年代的回收:老年代中对象存活时间长,回收频率低,使用 标记-整理算法标记-清除算法,更加适合老年代对象的特性。

且分区后可以减少 GC 暂停的时间(你想想每次处理一个堆的数据,还是将堆分区处理来的快)

总而言之,分区是为了更高效地管理不同生命周期的对象。

扩展知识

堆的分代机制

Java 堆内存根据对象生命周期被划分为三部分:

  • 新生代(Young Generation):存放新创建的对象。
  • 老年代(Old Generation):存放存活时间较长的对象,通常是从新生代晋升过来的对象。
  • 永久代(Metaspace)(JDK 8 以前为永久代,JDK 8 之后为元空间):存放类的元数据信息,包括类的静态变量、方法等。

新生代结构

新生代进一步划分为三个区域:

  • Eden 区:所有新创建的对象首先分配到 Eden 区。
  • Survivor 区:Eden 区中的存活对象会被复制到 Survivor 区(一般分为两个区域,S0 和 S1),经过多次 GC 存活的对象会逐渐晋升到老年代。

新生代中采用 复制算法,每次垃圾回收时,将 Eden 和 Survivor 中的存活对象复制到另一个 Survivor 空间,效率高且避免内存碎片。

老年代的作用

老年代用于存放生命周期较长的对象,通常是从新生代晋升过来的。老年代使用的回收算法不同于新生代,常用 标记-清除算法标记-整理算法,适合回收长生命周期的对象。

  • 标记-清除算法:遍历对象图,标记存活的对象,然后清除未标记的对象,但容易产生内存碎片。
  • 标记-整理算法:标记存活对象后,将存活对象整理到堆的一端,清理掉无效对象,避免了内存碎片问题。
Comments