JVM 中一次完整的 GC 流程是怎样的?
1 个回答
浪漫的灵魂从不向平淡的日子妥协。秦尘### 一、GC 的触发与对象分配 1. **对象分配** - 当对象被创建时,JVM 会根据对象的大小决定其分配的内存区域: - **大对象**:如果对象的大小超过了 `-XX:PretenureSizeThreshold` 参数设置的值(默认是 0),该对象将直接分配到 **老年代**(Old Generation)。 - **普通对象**:若对象大小未超过阈值,则会分配到 **年轻代**(Young Generation)的 **Eden 区**。 2. **Eden 区的分配** - 对象默认会分配到 **Eden 区**,如果 Eden 区的空间不足,JVM 将会触发 **Young GC**。 - 在 Young GC 之前,会进行 **空间分配担保**,以确认如果 Eden 区空间不足时,Survivor 区是否能够容纳需要存活的对象。 ### 二、Young GC 和空间分配担保 1. **Young GC**:当 Eden 区满时,JVM 会触发 Young GC。Young GC 使用的是 **标记-复制算法**: - **标记**:通过 GC Roots 标记存活的对象。 - **复制**:将存活的对象从 Eden 区和 From Survivor 区复制到 To Survivor 区。 - **清理**:清除 Eden 区和 From Survivor 区的对象,释放空间。 2. **空间分配担保**:在 Young GC 之前,JVM 会检查是否有足够的空间在 Survivor 区容纳存活的对象。如果空间不足,就会触发一次 **Full GC**,即老年代的垃圾回收。 3. **对象年龄判断**:如果对象在多次 Young GC 后仍然存活,它的年龄会增加,达到一定年龄后会被晋升到 **老年代**。 ### 三、新生代的区分与存活 - 新生代分为 **一个 Eden 区** 和 **两个 Survivor 区**(From Survivor 和 To Survivor)。这是为了优化对象存活和避免内存浪费: - **Eden 区**:用来存放新创建的对象。 - **Survivor 区**:用来存放经历过若干次 GC 后仍然存活的对象。 - 如果只有一个 Eden 区和一个 Survivor 区,那么就无法很好地实现标记-复制算法,且会造成内存空间的浪费。 ### 四、老年代垃圾回收(Full GC) 1. **Full GC 触发**:当年轻代的对象无法容纳存活的对象时,会触发 **Full GC**,如果老年代的空间不足或者空间分配担保失败,可能会导致 **OutOfMemoryError**(OOM)。 2. **老年代的 GC 算法**: - 现代的垃圾回收器通常使用 **三色标记法**,这种方法将回收过程分为以下四个阶段: - **初始标记**:标记所有从 GC Roots 可达的对象。 - **并发标记**:在应用线程运行的同时,标记所有可能的对象。 - **重新标记**:对并发标记阶段未标记到的对象进行标记。 - **并发清理**:清理无用的对象。 - 常见的老年代垃圾回收器有 **CMS**(Concurrent Mark-Sweep)和 **G1**(Garbage First)。 3. **Full GC 后空间不足**:如果在进行 Full GC 后老年代空间依然不够,可能会触发 **OOM**,即 **OutOfMemoryError**。 ### 五、总结 - **年轻代 GC(Young GC)**:基于标记-复制算法,涉及 **Eden 区** 和 **Survivor 区**,并通过空间分配担保机制避免内存溢出。 - **老年代 GC(Full GC)**:通过三色标记法进行清理,通常发生在年轻代的对象晋升到老年代后,老年代空间不足时会触发。 - **OOM(OutOfMemoryError)**:在 Full GC 后,如果空间依然不够,JVM 将抛出 **OOM** 错误。发布于 2025-02-19 14:56 •IP属地 河南举报评论收藏
公众号进入网站体验更佳

