java gc复制法为什么要用survivor

java gc复制法为什么要用survivor区?它在分配担保的时候发挥了什么作用?

复制法的出发点是为了提高效率、避免出现内存碎片,其实现方式为:将内存一分为二,对象申请内存时只使用其中一半,当GC时将活着的对象拷贝到另外一半内存,然后将现在使用的这段内存回收掉。 但复制法的代价太高了:内存只能用一半。实际上虚拟机在新生代的回收使用了复制法,但做了改进:由于新生代的内存在GC时可能只有极少数的对象还处于存活状态,所以复制目的地不需要“一半”这么大。HotSpot虚拟机以8:1的比例来分割内存,内存分为1块Eden区(8/10),2块Survivor区(各1/10),内存回收的时候将Eden+Survivor的存活对象拷贝到另一块Survivor区。如果不幸存活对象太多导致Survivor空间存储不下,虚拟机会从老年代进行分配担保。 这里有一个问题,为什么要设计2块Survivor区呢? 答案是很“显然”的,如果只有1块Survivor区,那么下次回收时,这块Survivor上还是有对象是活跃的,既然是复制法,那也只能复制到另外一块Survivor上了。考虑到Survivor空间不大,而更可能的是其对象在下次GC的时候需要被回收的可能性很高,所以也没有必要对Survivor区去做Mark-Sweep/Compact了。

survivor区每存活一次,年龄+1 加到一定程度的时候会统一复制到老年代

作用:

我们知道如果对象在复制到Survivor区时若Survivor空间不足,则会出发担保机制,将对象转入老年代;

但老年代的能力也不是无限的,因此需要在minor GC时做一个是否需要Major GC 的判断:

  • 如果老年代的剩余空间 < 之前转入老年代的对象的平均大小,则触发Major GC
  • 如果老年代的剩余空间 > 之前转入老年代的对象的平均大小,并且允许担保失败,则直接Minor GC,不需要做Full GC
  • 如果老年代的剩余空间 > 之前转入老年代的对象的平均大小,并且不允许担保失败,则触发Major GC

出发点还是尽量为对象分配内存。但是一般会配置允许担保失败,避免频繁的去做Full GC。