为什么 Java 的垃圾收集器将堆分为老年代和新生代?
为什么 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