浅谈新生代为什么要分三块区域并且比例为什么是8:1:1
如题最近网上看到了一个某大厂的面试题,新生代为什么分区网上答案比比皆是,为什么是8:1:1我是没搜到什么有价值的答案,今天结合这个题目谈谈自己的粗浅想法,如有不对还望指正;另外需要说明的是,接下来聊的都是基于G1之前的垃圾收集器;
首先,我们假设新生代如果不分代会发生什么,如果不分代的话那么堆内存就是一块新生代,一块老年代,当发生mionrGc时,收集器不管采用的是古老的Serial还是Parallel Scavenge,都会使用标记-复制算法将新生代中存活的对象直接复制到老年代;
这时候会产生如下问题:1.原先本来可作为担保空间的老年代直接用来容纳了每次gc存活的对象,导致了标记-复制算法没有了担保空间,大大新增了fullGC发生的概率和次数
2.无论对象年龄与否,只要熬过一次垃圾回收即可进入老年代,使进入老年代的成本降低,加大了老年代的负担,同样会增加fullGc的次数
所以由上可得,新生代必须分代,此时我们假设将新生代分成一个e区一个s区,如图:
我们来看下在此分代模式下进行mionrGC会发生什么,第一次gc时存活对象由e区进入s区然后清空e区,此时老年代也可以启担保作用,一切看起来都很正常;但是如果再发生第二次gc会怎样呢,此时e区有存活对象而容纳第一次gc时存活对象的s区也
肯定会有存活对象,此时需要将e,s区的存活对象复制到老年代,此时,问题也就显而易见了,与不分代的时候情形一样了,对象只能进入老年代;另外还有个比较严重的问题是,新生代分两区的话加上采用的是复制算法,那么不可避免的会产生垃圾碎片;
所以我们要将新生代再开辟一块区域来当作一个拦截器,不让不达年龄的对象直接接入老年代;结构如图:
此时新生代分成了e区,s0区和s1区;这时候再发生mionrGc就不会有上面出现的问题了:第一次GC时存活对象由e进入s0,然后清空e区;第二次gc时将e区和s0区的存活对象复制到s1区,清空e区和s0;第三次将e区和s1的对象复制到s0,清空e和s1。。。。。。
无论多少次mionrGc,如此反复,直到对象年龄达标或者s区(s0或s1)容不下存活对象时再晋升到老年代,这样也就降低了老年代的压力,减少了fullGc的次数;
以上的废话假设了各种不分块或分两块的利弊,其实在深入理解java虚拟机里接一小段话,大家可以参考下,就是P。页对标记-复制算法的讲解;
所以个人认为的新生代分分区或分三区的原因如下:1.降低老年代的内存分配压力,通过设置两个s区来对年轻对象进行拦截,降低fullGc的次数
2.分三代能使老年代作为担保来应付s区容不下存活对象的情况
接下来我们来讨论下为什么e:s0:s1默认是8:1:1?
深入理解虚拟机一书里有描述,IBM的一项研究,表明新生代中有98%的对象是朝生夕灭的,换言之,每次mionrGC后存活的对象应该小于等于2%,所以看起来采用复制算法的新生代似乎可以不用将内存分成大小相等的两块了,但考虑到实验偏差以及实际情况的多样性,jvm
默认预留了10%的内存用于存放存活对象,此时结合上文描述的,新生代最优应该分成三块,所以得再预留一块10%的内存给s区,那么自然剩下的80%就是e区的大小了;