内存分配与回收策略
1、对象优先在Eden分配
前几篇文章中提到过,java堆的新生代中分有一个Eden区,和两个Survivor区,默认情况下两者比例为8:1。当新的对象进来时,会优先在Eden区上为对象分配空间。如果此时Eden的大小并不足以放进这个对象,则会出发一次新生代的垃圾回收,将Eden和Survivor中的还存活的对象全部复制到另一块Survivor中,然后在Eden中分配。
2、大对象直接进入老年区
大对象,就是指回占用大片内存空间的对象,典型的就是很长很长的字符串或是数组。对于这类对象,虚拟机会直接把它放在老年代,在老年代为它分配内存。这样做的目的就是为了尽量避免在新生代进行来及回收。虚拟机可以有一个参数设置更改这个大小,来决定具体到多少内存的对象就会在老年代申请内存了,我会在最后一次介绍虚拟机的文章中来总结所涉及到的参数。
3、长期存活的对象将进入老年代
虚拟机为每一个对象都设定了一个“年龄”的计数器,这是计数器起始为0,当新生代进行一次来及回收后,对象仍然存活,并且可以被Survivor所接纳,那么将被存入Survivor区,并且年龄计数器加1。以后每熬过一次垃圾收集都将会加1,直到加到一定是指就会转入到老年代,默认为15岁,也可以用参数更改这个数值。
4、动态对象年龄判断
为了更好的适应不同程序的额内存状况,通过年龄晋升到老年代也不是那么绝对。当Survivor中的存活的相同年龄的对象大小,等于或大于Survivor的一半,就可以把这些对象都放到老年代中。
5、空间分配担保
前文提到,新生代垃圾回收的算法是复制算法,将Eden和Survivor中的还存活的对象全部复制到另一块Survivor中,这时问题就来了,如果另一块Survivor的大小不够了怎么办?这时就用到了空间分配担保。
在每一次新生代垃圾收集之前,都会检测一下老年代的空余大小是否大于等于新生代中所有的对象大小,如果是,则继续进行收集,一旦Survivor无法装下对象后,就会将一部分放入老年代中。如果老年代空余空间比新生代对象小,这是虚拟机就会查看是否允许冒险分配,是否允许担保失败,如果允许,则继续进行,不允许,就要先进行一次老年代的垃圾收集。
所谓冒险,就是老年代会承受分配失败的风险,如果允许冒险,则老年代会根据之前晋升到老年代的对象大小的平均值来与老年代剩余空间作比较,决定用不用进行老年代的垃圾收集。如果冒险失败,那就只好在进行一次垃圾收集了。