一、堆空间内存结构

java05
堆的内存空间分为新生代和老年代,默认占比是新生代占1/3,老年代占2/3,可以通过-XX:NewRatio来调整比例。而新生代又划分了3个区域,分别是Eden区、from survivor区和to survivor区,默认占比是Eden区占8/10,from survivor区占1/10,to survivor区占1/10,可以通过-XX:SurvivorRatio来调整比例。

例如现在堆内存是1500MB,那么每个区域的内存大小分别是:
老年代:1500MB * 2/3 = 1000MB
新生代:1500MB * 1/3 = 500MB

  • Eden区:500MB * 8/10 = 400MB
  • from survivor区:500MB * 1/10 = 50MB
  • to survivor区:500MB * 1/10 = 50MB

二、GC之后对象何去何从?

(1)一般情况
java06
我们知道新生代划分了一个Eden区和两个Survivor区,其中Survivor区用于存放GC之后还存活的对象。

默认情况下对象存放在Eden区,当Eden区内存不足触发Minor GC之后,JVM会把存活的对象存放到Survivor区中,正常情况下,每次执行GC,都能清除掉90%以上垃圾,存活对象不到10%,下一次执行Minor垃圾时,会把上一次执行GC过后的Eden区和Survivor区进行GC,然后把存活对象复制到另外一个Survivor区,每执行一次GC之后,对象的年龄+1,当对象年龄达到15岁时,该对象进入到老年代。(注:这个岁数可以通过-XX:MaxTenuringThreshold来调整)

(2)执行Minor GC之后survivor区的对象占比超过50%
执行Minor GC之后,一般情况下存活对象存放在Survivor区中,如果这个时候Survivor区对象占比超过50%时,那么年龄大于1岁的对象将迁移到老年代。

(3)执行Minor GC之后存活对象超过Survivor区大小
执行Minor GC之后,如果存活对象的大小超过了Survivor区的大小,那么这批对象将直接分配到老年代中。

(4)执行Minor GC之后存活对象超出了老年代可用空间
执行Minor GC之后,如果存活对象的大小超过了老年代可用空间,那么将触发一次Full GC,如果Full GC执行之后,还是空间不足的话就抛出OOM。

(5)大对象怎么分配
默认情况下,对象大小超过1MB的称之为大对象,可以通过-XX:PretenureSizeThreshold来调整定义。
对于大对象,通常都是数组之类的对象,那么JVM直接将大对象分配到老年代中。

三、空间担保

JVM为了保证每次执行Minor GC之后,存活对象有地方可以存放,在执行Minor GC之后,会检查老年代是否有足够空间可以存放新生代的对象,极端情况下,一次Minor GC过后,新生代所有的对象都存活,因此JVM默认将检查老年代的空间是否足够新生代中的对象。

如果执行Minor GC之前,老年代的可用空间已经小于新生代中对象的大小了,那么将进一步简单JVM是否有设置-XX:handlePromotionFailure参数,如果设置了该参数,则会计算之前Minor GC之后进入到老年代对象的平均大小,比如执行Minor GC之前对象大小为100MB,执行Minor GC之后对象大小为10MB,那么这个时候只需要计算老年代可用空间是否大于10MB即可,而不用去管100MB的事。但是如果没有设置-XX:handlePromotionFailure参数,那么将执行一次Full GC。

打赏
支付宝 微信
上一篇 下一篇