为什么新生代需要有两个Survivor区

转载自:http://blog.csdn.net/antony9118/article/details/51425581

1.为什么要有Survivor区

  首先思考设置Survivor区的意义在哪里?
  如果没有Survivor,Eden区每进行一次Minor GC,存活的对象就会被送到老年代。老年代很快被填满,触发Major GC(也可以看做触发了Full GC)。老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多。频发的Full GC消耗的时间是非常多的,这一点会影响大型程序的执行和响应速度。

  那来想想在没有Survivor的情况下,有没有什么解决办法,可以避免上述情况:

  1. 增加老年代空间
    使更多的存活对象才能填满老年代,降低Full GC频率,但随着老年代空间加大,一旦发生Full GC,执行所需要的时间更长。
  2. 减少老年代空间
    使Full GC所需时间减少,但老年代会很快被存活对象填满,Full GC频率增加。

  显然,没有Survivor的话,上述两种解决方案都不能从根本上解决问题。

  可以得到第一条结论:Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历几次Minor GC还能在新生代中存活的对象,才会被送到老年代。

2.为什么要设置两个Survivor区

  设置两个Survivor区最大的好处就是解决了碎片化。
  下面来分析一下,为什么一个Survivor区不行?假设现在只有一个survivor区,一旦Eden满了,触发一次Minor GC,Eden中的存活对象就会被移动到Survivor区,这样继续循环下去。问题来了,进行Minor GC时,Eden和Survivor各有一些存活对象,把Eden区的存活对象放到Survivor区,Survivor区的过期对象移除,很明显操作之后Survivor区对象所占有的内存是不连续的,也就导致了内存碎片化。
  碎片化带来的风险是极大的,严重影响JAVA程序的性能。堆空间被散布的对象占据不连续的内存,最直接的结果就是,堆中没有足够大的连续内存空间,就会触发Major GC。
  那么,如果建立两块Survivor区,刚刚新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1(这种复制算法保证了S1中来自S0和Eden两部分的存活对象占用连续的内存空间,避免了碎片化的发生)。S0和Eden被清空,然后下一轮S0与S1交换角色,如此循环往复。如果对象的复制次数达到阈值,该对象就会被送到老年代中。
  上述机制最大的好处就是,整个过程中,永远有一个survivor space是空的,另一个非空的survivor space无碎片。

猜你喜欢

转载自blog.csdn.net/zero__007/article/details/85107746