Minor GC、Major GC和Full GC的区别
Minor GC、Major GC和Full GC的区别
小吴顶呱呱堆内存图:
⚠️这里的堆是指逻辑上的堆,也就是包括方法区(元空间)
GC的分类
JVM在进行GC时,并非每次都对上面的三个内存(新生代、老年代和方法区)区域一起回收的,大部分时候回收的都是指新生代。
针对HotSpot VM的实现,它里面的GC按照回收区域又分为俩大种类型:
- 一种是部分收集(Partial GC)
- 一种是整堆收集(Full GC)
-
部分收集:不是完整的收集整个java堆的垃圾收集。其中可分为:
-
新生代收集(Minor GC/Young GC):只是新生代(Eden,s0,s1)的垃圾回收,注意只有当Eden区满的时候才会进行回收。
-
老年代收集(Major GC/Old Gc):只是老年代的收集。
❗目前只有CMS垃圾回收器会有单独收集老年代的行为,因为老年代的回收往往伴随着新生代的回收,正因为此,很多时候Major GC会和Full GC混淆使用,需要具体分辨是老年代回收还是整堆回收。
-
混合收集(Mixed GC):收集整个新生代以及部分老年代的垃圾收集。目前只有G1垃圾回收器会有这种行为
-
-
整堆回收(Full GC):收集整个java堆和方法区的垃圾收集。
以上内容出自周志明老师的《深入理解java虚拟机(第三版)》:
年轻代GC
年轻代GC(Minor GC)触发机制:
- 当年轻代空间不足时,就会触发Minor GC。这里的年轻代满指的是Eden区满,Survivor满并不会触发GC(每次Minor GC都会清理年轻代的内存)
- 因为java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也很快。
- Minor GC会引发STW(stop the world),暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行。
老年代GC
老年代GC(Major GC/Full GC)触发机制:
- 指发生在老年代的GC。对象从老年代消失时,我们说的"Major GC" 或“Full GC”发生了。
- 出现了Major GC,经常会伴随至少有一次的Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。
- 也就是在老年代空间不足时,会先尝试触发Minor GC。如果之后空间还不足,则触发Major GC。
- Major GC的速度一般会比Minor GC慢10倍以上,STW的时间更长。
- 如果Major GC之后,内存还不足就报OOM。
整堆GC
Full GC触发机制:
-
调用System.gc()时,系统建议执行Full GC,但是不必然执行
-
老年代空间不足
-
方法区空间不足
-
通过Minor GC后进入老年代的平均大小大于老年代的可用内存
-
有Eden区、survivor space0(from space)区向survivor space1(to space)区复制时,对象大小大于to space可用内存,则吧该对象转存到老年代,且老年代的可用内存小于该对象。
⚠️full GC是开发或者调优中尽量避免的。这样暂停时间会短一些。
System.gc()说明:
- 在默认情况下通过System.gc()或者Runtime.getRuntime().gc()的调用,会显式触发full GC,同时对老年代和新生代进行回收,尝试释放被丢弃对象占用的内存。
- 然而System.gc()调用附带一个免责声明,无法保证对垃圾收集器的调用。
- jvm实现者可以通过system.gc()调用来决定JVM的GC行为。而一般情况下,垃圾回收应该是自动进行的,无需手动触发。否则就太过于麻木了。在一些特性下,如我们正在编写一个性能基准,我们可以在运行之间调用system.gc()。
Major GC vs Full GC
大家应该注意到,目前,这些术语无论是在 JVM 规范还是在垃圾收集研究论文中都没有正式的定义。但是我们一看就知道这些在我们已经知道的基础之上做出的定义是正确的,Minor GC 清理年轻带内存应该被设计得简单:
- Major GC 是清理永久代。
- Full GC 是清理整个堆空间—包括年轻代和永久代。
很不幸,实际上它还有点复杂且令人困惑。首先,许多 Major GC 是由 Minor GC 触发的,所以很多情况下将这两种 GC 分离是不太可能的。另一方面,许多现代垃圾收集机制会清理部分永久代空间,所以使用“cleaning”一词只是部分正确。
这使得我们不用去关心到底是叫 Major GC 还是 Full GC,大家应该关注当前的 GC 是否停止了所有应用程序的线程,还是能够并发的处理而不用停掉应用程序的线程。