其中Eden区占80%内存空间,每一块Survivor区各占10%内存空间,比如说Eden区有800MB内存,每 一块Survivor区就100MB内存,
平时可以使用的,就是Eden区和其中一块Survivor区,那么相当于就是有900MB的内存是可以使用的,
但是刚开始对象都是分配在Eden区内的,如果Eden区快满了,此时就会触发垃圾回收。
此时就会把Eden区中的存活对象都一次性转移到一块空着的Survivor区。接着Eden区就会被清空,然后再次分配新对象到Eden区里,Eden区和一块Survivor区里是有对象的,其中Survivor区里放的是上一次Minor GC后存活的对象。
如果下次再次Eden区满,那么再次触发Minor GC,就会把Eden区和放着上一次Minor GC后存活对象的Survivor区内的存活对象,转 移到另外一块Survivor区去。
因为新生代的对象都是朝生暮死,存活率很低,留个100MB的Survivor区来保存存活的对象是足够的。
万一垃圾回收过后,存活下来的对象超过了10%的内存空间,在另外一块Survivor区域中放不下咋整?
答:当前放对象的Survivor区域里,一批对象的总大小大于了这块Survivor区域的内存大小的50%,那么此时大于等于这批对象年龄的对象,就可以直接进入老年代了。
具体来说就是假设Survivor2区有两个对象,这俩对象的年龄一样,都是2岁,然后俩对象加起来超过了50MB,超过了Survivor2区的100MB内存大小的一半了,这个时候,Survivor2区里的大于等于2岁的对 象,就要全部进入老年代里去。
万一我们突然分配了一个超级大的对象,大到啥程度?新生代找不到连续内存空间来存放,此时咋整?
答:大对象直接进入老年代,有一个JVM参数,就是“-XX:PretenureSizeThreshold”,可以把他的值设置为字节数,比如“1048576”字节,就是1MB。 如果大对象不直接进入老年代而是在新生代不停的移动,来回复制,那肯定会浪费时间浪费资源。
到底一个存活对象要在新生代里这么来回倒腾多少次之后才会被转移都老年代去?
答:对象每次在新生代里躲过一次GC被转移到一块Survivor区域中,此时他的年龄就会增长一岁,默认的设置下,当对象的年龄达到15岁的时候,也就是躲过15次GC的时候,他就会转移到老年代里去。这个具体是多少岁进入老年代,可以通过JVM参数“-XX:MaxTenuringThreshold”来设置,默认是15岁。
Minor GC后的对象太多无法放入Survivor区怎么办?
现在有一个比较大的问题,就是如果在Minor GC之后发现剩余的存活对象太多了,没办法放入另外一块Survivor区怎么办?
如果新生代里有大量对象存活下来,确实是自己的Survivor区放不下了,必须转移到老年代去,如果老年代放不下呢?
担保原则:(minor gc之前检查老年代可用空间大小)
1、当老年代可用空间 > 新生代所有对象的总大小
此时就可以放心大胆的对新生代发起一次Minor GC了,因为即使Minor GC之 后所有对象都存活,Survivor区放不下了,也可以转移到老年代去。
2、当老年代可用空间 < 新生代所有对象的总大小
意思就是当minro gc后如果所有对象都存活了,Survivor区放不下了,肯定放不到老年代里,这时候还需要看一个参数 “-XX:- HandlePromotionFailure”的参数是否设置了 ,如果有这个参数,
那么进行下一步判断:
看看老年代的内存大小,是否 > 之前每一次Minor GC后进入老年代的对象的平均大小。举个例子,之前每次Minor GC后,平均都有10MB左右的对象会进入老年代,那么此时老年代可用内存大于10MB。这就说明,很可能这次Minor GC过后也是差不多10MB左右的对象会进入老年代,此时老年代空间是够的。
如果上述判断的结果是:老年代的空间 < 之前每一次Minor GC后进入老年代的对象的平均大小,或者参数-XX:- HandlePromotionFailure”没有设置。
此时就会直接触发一次“Full GC”,
就是对老年代进行垃圾回收,尽量腾出来一些内存空间,
然后再执行Minor GC。
第一种可能,
Minor GC过后,剩余的存活对象的大小,是小于Survivor区的大小的,
那么此时存活对象进入Survivor 区域即可。
第二种可能,
Minor GC过后,剩余的存活对象的大小,是大于 Survivor区域的大小,
但是是小于老年代可用内存大小 的,此时就直接进入老年代即可。
第三种可能,触发 full gc
很不幸,Minor GC过后,剩余的存活对象的大小,大于了Survivor区域的大小,也大于了老年代可用内 存的大小。此时老年代都放不下这些存活对象了,就会发生“Handle Promotion Failure”的情况,这个时候就会触 发一次“Full GC”。
老年代满了 OOM 有两种可能:
1、老年代本身对象比较多,没办法回收,都是强引用
2、新生代minor gc 后对象的大小 > survive区 ,所以进入老年代,这时候老年代也放不下这些对象,所以OOM。
其实把上面的内容都看懂之后,大家现在基本就知道了Minor GC的触发时机,然后就是Minor GC之前要对老年代空 间大小做的检查。
包括检查失败的时候要提前触发Full GC给老年代腾一些空间出来,或者是Minor GC过后剩余对象太多放入老年代内 存都不够,也要触发Full GC。包括这套规则,还有触发老年代垃圾回收的Full GC时机,都给大家讲清楚了。
简单来说,一句话总结,对老年代触发垃圾回收的时机,一般就是两个:
1、在Minor GC之前,一通检查发现很可能Minor GC之后要进入老年代的对象太多了,老年代放不下,此时需 要提前触发Full GC然后再带着进行Minor GC;
2、在Minor GC之后,发现剩余对象太多放入老年代都放不下了。
一个之前,一个之后,好好理解。
其实大家如果透彻理解了最近的几篇文章涵盖的JVM的运行原理,就会知道,所谓JVM优化,就是尽可能让对象都在 新生代里分配和回收,尽量别让太多对象频繁进入老年代,避免频繁对老年代进行垃圾回收,同时给系统充足的内存 大小,避免新生代频繁的进行垃圾回收。
如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!
加入交流群
请使用微信扫一扫!