JVM的分代垃圾回收(GC)机制是如何设计的?
1 个回答
不要在赚钱的路上把健康丢了,要在健康的路上顺便把钱赚了。美啦美啦美啦## 作用 JVM 的分代垃圾回收是根据对象生命周期规律,把堆划分为不同区域,并为不同区域选择合适的回收算法,从而在回收频率、停顿时间和内存碎片之间取得平衡。 - **新生代**:存放朝生夕死的新对象,目标是快速、高频回收。 - **老年代**:存放长期存活的对象,目标是低频、稳定回收。 - **跨代引用处理**:避免回收新生代时扫描整个老年代,降低 GC 成本。 ## 原理 ### 1. 基于弱分代假说设计新生代 弱分代假说认为:大多数对象生命周期很短,第一次 GC 时就会死亡。 - 新生代通常划分为: - `Eden` - 两个 `Survivor` 区:`S0` 和 `S1` - 新对象优先分配在 Eden。 - 新生代发生 **Minor GC / Young GC** 时,通常使用**标记-复制算法**: - 标记存活对象; - 将存活对象复制到空的 Survivor 区; - 清空 Eden 和原来的 Survivor 区。 - 对象每熬过一次 Minor GC,年龄增加 1。 - 年龄达到阈值后晋升到老年代,常见默认阈值是 15。 这种方式适合存活率低的对象,复制成本较低,也能减少内存碎片。 ### 2. 基于强分代假说设计老年代 强分代假说认为:熬过多次回收的对象通常会继续存活,例如单例对象、缓存、静态集合等。 - 老年代对象存活率高,不适合频繁复制。 - 老年代通常采用: - **标记-清除算法**:实现简单,但可能产生内存碎片; - **标记-整理算法**:回收后移动存活对象并压缩空间,可减少碎片。 - 老年代 GC 频率较低,但回收范围更大,停顿通常也更长。 - 触发原因可能包括: - 老年代空间不足; - 新生代对象晋升失败; - 元空间不足; - 显式调用 `System.gc()` 等。 ### 3. 基于跨代引用假说处理代间引用 跨代引用假说认为:老年代引用新生代对象的情况较少,但 Minor GC 又必须识别这些引用。 如果每次 Minor GC 都扫描整个老年代,代价会很高,因此 JVM 使用: - **记忆集(Remembered Set)**:记录跨代引用信息。 - **卡表(Card Table)**: - 将老年代划分为固定大小的卡,例如每张卡 512 字节; - 如果某个卡中存在指向新生代的引用,就将该卡标记为“脏卡”。 - **写屏障(Write Barrier)**: - 在对象引用赋值时维护卡表; - 当老年代对象引用新生代对象时,把对应卡标记为脏。 Minor GC 时,只需要把脏卡中的老年代对象也作为 GC Roots 的一部分扫描,而不用遍历整个老年代。 ### 4. 分代设计的扩展 - 可通过参数调整各代比例,例如: - `-XX:NewRatio`:控制新生代和老年代的比例; - `-XX:SurvivorRatio`:控制 Eden 和 Survivor 的比例。 - G1 不再采用连续的物理分代,而是用多个 Region 组织堆空间,但仍保留逻辑分代,并可执行 Young GC、Mixed GC。 - ZGC、Shenandoah 等低延迟收集器进一步强化并发标记和并发整理,以降低停顿时间。 因此,JVM 分代 GC 的核心设计思想是:**按对象存活时间分区,新生代用复制算法追求效率,老年代用标记-清除或标记-整理算法降低复制成本,再通过记忆集、卡表和写屏障高效处理跨代引用。**发布于 2026-10-29 11:11 •IP属地 湖南举报评论收藏
公众号进入网站体验更佳

