大话Minor GC与Full GC分别在什么时候发生?

Java堆分区

要理解Minor GC和Full GC之前要先了解Java堆的分区。如果对Java堆进行细分地话,又可以分为新生代(包含Eden空间、From Survivor空间、ToSurvivor空间)和老年代。

在这里插入图片描述

注意:这样要补充一点。在HotSpot虚拟机中默认Eden和From Survivor、ToSurvivor的大小比例是8:1:1。所以上图为了展示清楚,画的不是很准确。

垃圾收集分类

在这里插入图片描述

我们采用拟人的手法,把整个Java堆比喻成一个大房子,虚拟机比喻成房子的主人小V,来看看垃圾收集是怎么一回事。

还有上面三个空间的名字太长了,我们这样约定一下:Eden空间——伊甸空间,From Survivor空间——from空间,To Survivor空间——to空间

Minor GC

小V每次买了好多东西(new了很多对象)都喜欢放在伊甸+from这个房间,“哼~~你管我,我就是这个决定的”。房子虽然是豪宅但是也耐不住小V更豪,伊甸+from房间总有装满的一天。但是他又想买东西了,这个时候就只能把一些对他不重要的东西丢掉(Minor GC),好腾出空间放新东西。

即:大多数情况下,对象在新生代的Eden区中分配(更准确是在Eden+From Surivivor区分配)。当Eden区没有足够空间进行分配时,虚拟机将进行一次Minor GC。

Full GC

进行Full GC的情况有多种。

1. 调用System.gc()

这一天小V的母亲建议他收拾一下房间,但是对于收不收拾房间,小V显示有自己的想法。

即:调用System.gc()方法只是建议垃圾收集器进行Full GC,而垃圾收集器进不进行收集是不确定的。

2. 大象

这一次小V买了一头大象,这真是实名副其实的大对象。这头大象对小V来说决对是一个让他头疼的事,主要有下面两个理由。

一、即便伊甸+from房间还有很多空间,但是这头大象一来,他很大可能就要开始收拾房间了(Minor GC);二、后期还有可能要把这头大象搬到to房间或老年代去,简直欲哭无泪啊!!!

那么干脆直接放到老年代啊,但是往老年代放也不一定放的下啊,这时会进行Full GC好腾出更多的空间存储大象。

即:HotSpot虚拟机提供了-XX:PretenureSizeThreshold参数,指定大于该设置值的对象直接在老年代分配。但是老年代不一定放的下,这时需要进行Full GC好腾出更多的空间。

3. 分配担保

小V收拾伊甸+from房间的方法是这样的,如果发现了好多对他还有用的东西(就是这个对象还有用,更准确地描述为按照可达性分析算法到GC roots是可达的)。

对于这些东西,他处理的方式是这样的,to房间放得下就是放to房间,to房间放不下就放老年代房间。于是又是同样的问题,老年代也不一定放的下啊。

那么这就不仅仅是老年代放不放的下的问题了,因为Minor GC后,to房间放不下的一定要往老年代放,所以能不能进行Minor GC都是问题。

对于这个问题不同年龄段的小V有不同的处理方式。六岁零24天之后,他是这么做的,比较一下老年代房间剩下的空间和新生代房间所有物品所占的空间,如果老年代大,没啥说的,收拾起来;如果老年代小,Full GC。

即:JDK 6 Update 24之后的规则是这样的,只要老年代的连续空间小于新生代对象总大小,就进行Full GC。至于在该版本之前的处理方式比较复杂,这里不详细说明。

4. CMS收集器Concurrent Mode Failure

身为理工男的小V是个电子发烧友,而且人家很耐烧,对此他做了很多种帮他收拾垃圾的机器人——CMS智能垃圾收集机器人只是其中的一种。

CMS收集器的垃圾收集过程可以分为四个步骤:
(1)初始标记
(2)并发标记
(3)重新标记
(4)并发清除

并发清除的意思是,清理垃圾和用户线程是并发进行的,既然用户线程正在进行就会产生新的对象,而且这些新产生的对象还完美逃避了本轮的标记即不会被回收。

如果这些对象要存入老年代,而老年代又空间不够,此时就会出现“Concurrent Mode Failure”进而导致Full GC的产生。

5. 永久代空间不足

关于永久代只有Sun/Oracle JDK和Open JDK中默认使用的HotSpot虚拟机中才有这个概念,其他虚拟机比如J9、JRockit中是没有的。

在JDK8之前,当时的HotSpot虚拟机设计团队,不但在Java堆的垃圾收集上使用了“分代设计”思想,还将其扩展到了方法区即用永久代来实现方法区,这样HotSpot中的垃圾收集器就能像管理Java堆一样管理方法区,所以当永久代空间不足时同样会导致Full GC。

这里补充一下永久代的历史,有兴趣的可以看看。其实用永久代来实现方法区并不是一件好事,因为这样更容易导致方法区内存溢出(永久代有-XX:MaxPermSize上限,即使不设置也有默认大小,而对于不使用永久代的虚拟机来说,只要不超过机器内存就不会有问题。)

所以从JDK6开始HotSpot就有放弃永久代的计划,到JDK7时期,已经将字符串常量池和类的静态变量移到了Java堆,终于到了JDK8之后这一概念被完全抛弃,改用元空间来实现方法区,永久代成了永久的回忆。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值