首先看java堆的模型:新生代 80%的eden区和两个各10%的suvivor区。
首先,在程序不断运行过程中新建的对象都在Eden区,当Eden满后,做monir GC,将存活的对象放到Survivor1。当Eden和Survivor1都满后做minor GC放到Survivor2。当Eden和Survivor2都满后再做minor GC,将存活对象放到Survivor1。如此反复。
何时进入老年代?
1、到过15次minor gc后,将对象从新生代放入老年代。这里的15次可以设置。
2、动态年龄判断:年龄1+年龄2+...+年龄N>50%的survivor区域时,把年龄N以上的数据放入老年代。说白了就是将长期存活的对象放到老年代中。
3、大对象:比如大对象或者数组直接放到老年代。多大的对象算是大对象,可以设置。这么做的目的是避免大对象屡次多过minor GC,多次在survivor1、survivor2之间来回拷贝。
4、minor GC后存活对象太多,大于survivor1的空间
何时full GC
full gc是指老年代空间满的时候,进行垃圾回收。也就是说minor gc后survivor放不下,要放到老年代,此时发现老年代也放不下了,会触发full gc。
触发条件
1、在每一次minor gc前会检查老年代可用空间大小,如果老年代可用空间大小大于新生代对象总和,那么直接minor gc,不用full gc。因为即使新生代全部对象都存活下来老年代依旧可以放下。
2、加入条件1检测发现老年代可用空间已经小于新生代全部对象大小,这时候该怎么办呢?因为理论上新生代minor gc后对象是有可能全部存活下来并放到老年代的。
下一步就要判断老年代可用空间大小是否大于之前每一次minor gc后进入老年代的大小了。举个例子,比如之前每次进入老年代的大小是10M,此时老年代可用空间小于10M,那么就直接触发FULL GC。
如果老年代可用空间大于10M,也就是进入老年代的大小,那么就冒险先做一次minor gc。接下来有几种可能:
1、做完minor gc后如果发现存活对象小于survivor大小,直接放入survivor
2、大于survivor大小,小于老年代可用空间大小,直接放入老年代
3、很不幸,老年代可用空间大小也不够用了,直接触发full gc。如果full gc后发现老年代可用空间依旧不够用,那么就会OOM,进程崩溃。
老年代垃圾回收算法
简单来说就是标记整理算法。
首先标记出当前存活的对象,然后挪到老年代前面,让存活对象紧凑一些,避免垃圾回收过程中产生很多内存碎片。值得注意的是老年代垃圾回收算法比新生代垃圾回收算法至少慢十倍,所以full gc越频繁,服务卡顿越明显。
为什么full gc会比minor gc慢呢?因为full gc是在一个内存区域内挪动垃圾对象和存活对象,minor gc算法不用在一个区域内做,而是标记完后直接copy到survivor区域内。
转载自full GC触发的条件