JVM 中一次完整的 GC 流程(从 ygc 到 fgc)是怎样的

574 阅读4分钟

JVM 中一次完整的 GC 流程(从 ygc 到 fgc)是怎样的

juejin.cn/post/699538…

image.png

答:

JVM中一次完整的垃圾回收(GC)流程,通常包括两个部分:年轻代GC和老年代GC。其中年轻代GC又称为YGC(Young Generation Garbage Collection),老年代GC又称Major GC(Old GC)。下面,我将逐步介绍一次完整的GC流程。

  1. YGC

当应用程序需要创建新对象时,JVM会将对象分配到年轻代内存中。年轻代内存通常被分为3个区域:一个Eden空间和两个Survivor空间。大多数新对象被分配到Eden空间,当进行垃圾回收时,首先先标记哪些是需要被回收的对象,然后将幸存的对象移动到其中一个survivor区间中(移动的过程中会对对象进行排序,所以在物理空间中,对象是连续的,所以访问效率会很高,使用哪个survivor空间,就将哪个survivor空间标记为使用状态),然后直接清除eden区间;当再次进行垃圾回收时,将eden区间中幸存的对象以及使用中的survivor空间的对象移动到另外一个survivor空间中,然后清空eden和原来的survivor空间;在下一次 Young GC 时,需要交换两个 Survivor 区域的角色,即将被使用的 Survivor 区域变为保持空闲的区域,而空闲的 Survivor 区域则变为被使用的区域。这样做的目的是避免在 Survivor 区域中产生内存碎片,并尽可能利用可用的内存空间。)记录这些对象的存活情况,并更新相关的计数器。当默认达到15岁就会移动到老年代中,或者当某一年龄的对象占用的总空间大于当前survivor空间的一半时,就会将该年龄以及以上的对象移动到老年代中。(为什么要移动到老年代,老年代的作用时什么?:当对象经过多次gc还没被回收,可以认为该对象需要长期存在,既然需要长期存在,那么就没有必要让垃圾回收器反复的扫描它,不利于提高效率;老年代gc扫描的频率较低)

  1. FGC

当年轻代区域不能容纳新分配的对象时,JVM会将对象分配到老年代内存中。老年代内存通常比年轻代内存更大,并且它们被分配在JVM堆的后半部分。当老年代内存区域达到一定的使用率时,JVM会执行FGC。FGC期间,JVM会执行以下步骤:

  • 执行根搜索算法,标记所有从根对象(如线程栈和静态变量)开始可达的对象。
  • 将未标记的对象标记为垃圾对象。
  • 执行垃圾回收算法,释放所有被标记为垃圾对象占用的内存空间。
  • 执行内存整理,将所有存活的对象向一端移动,以便更容易分配连续的内存空间。

整个GC流程通常会涉及多个线程,包括标记线程、垃圾回收线程和整理线程。由于GC可能会占用大量的系统资源,因此在生产环境中需要精心管理和配置JVM,以确保它能够高效地运行并最大限度地减少系统停顿时间。

在发生Minor GC(Young GC)之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者HandlePromotionFailure设置 不允许冒险,那这时也要改为进行一次Full GC。

空间担保原则:

image.png 老年代触发垃圾回收的时机,一般就是两个:

  1. Minor GC之前发现要进入老年代的对象太多,装不下,触发Fu'll GC 再带着进行Minor GC
  2. Mionr GC过后,剩余对象太多老年代存放不下,触发Full GC