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