本文主要是介绍为什么新生代有两个Survivor分区,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
分代收集器会把内存空间分为:老年代和新生代两个区域,而新生代又会分为:Eden区和两个Survivor区(From Survivor、To Survivor)。
可以看出,Eden区和Survivor分区的默认比例是8:1:1。这个值可以通过:-XX:SurvivorRatio设定,默认值,:-XX:SurvivorRatio=8。
新生代和老年代的默认情况下内存占比是1:2,该值可以通过:-XX:NewRatio来设定。
1.为什么要有Survivor区
如果没有Survivor,Eden区每进行异常Minor GC,存活的对象就会被送到老年代。老年代很快被填满,触发Major GC(因为Major GC一般伴随着Minor GC,也可以看做是触发了Full GC。老年代的内存空间大于新生代,进行一次Full GC消耗的时间比Minor GC长的多。
Full GC会影响大型程序的执行和响应速度,更不要说某些连接会因为超时发生连接错误了。
如果没有Survivor的情况下,如何避免Full GC的频繁发生呢。
- 增加老年代空间。
优点:更多存活对象才能填满老年代。降低Full GC频率。
缺点:随着老年代的空间加大,一旦发生Full GC,执行所需要的时间更长。 - 减少老年代空间
优点:Full GC所需时间减少
缺点:老年代很快会被存活对象填满,Full GC频率增加。
结论:Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历过16次的Minor GC还能在新生代中存活的对象,才会被送到老年代。
为什么要设置两个Survivor区呢
设置两个Survivor区的最大好处就是解决了碎片化。
如果只有一个Surivor区:
刚刚新建的对象存放在Eden区,一旦Eden区满了,就会触发一次Minor GC,Eden中的存活对象就会被移动到Survivor区。这样继续循环下去,下一次Eden满了的时候,问题来了,此时进行Minor GC,Eden和Survivor各有一些存活对象,如果此时把Eden区的存活对象硬放到Survivor区,很明显这两部分对象所占有的内存是不连续的,也就导致了内存碎片化。
碎片化带来的风险是极大的,严重影响Java程序的性能。堆空间被散布的对象占据不连续的内存,最直接的结果,就是堆中没有足够大的连续内存空间,接下去如果程序需要给一个内存需求很大的对象分配内存,那么就会需要整理空间。
因此,需要建立两块Survivor区,刚刚新建的对象存放在Eden区,经历过一次Minor GC后,Eden中的存活对象就会被移动到第一块的survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中存活的对象就会被复制送入到第二块的survivor space S1,避免了碎片化的发生。
S0和Eden被清空后,然后进行下一轮S0和S1交换角色,原S0变成S1,原S1变成S0。如此循环往复,当survivor区中有对象的复制次数达到16次,也就是16次 Minor GC都没有回收该对象,该对象就会被送到老年代。
上述机制的最大好处就是,整个过程中,永远有一个survivor是空的,另一个非空的survivor space没有碎片。
那么为什么survivor为什么不分更多块呢
如果Survivor区再细分的话,每一块的空间就会很小,很容易导致Survivor区满,而且两块的Survivor区,已经足够支持标记-复制算法的进行。应该是经过权衡之后的结果。
https://blog.csdn.net/antony9118/article/details/51425581
这篇关于为什么新生代有两个Survivor分区的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!