前言
在深入了解JAVA虚拟机书中,虽然对垃圾回收进行了详细的说明,并且列举了很多主流的垃圾回收器,但是对于分代算法中具体新生代和年老代如何划分自己的内存空间、何时进行GC、新生代是如何转化为年老代等诸如此类的问题没有进行详细的说明,所以,在这篇文章里将简短地做一个总结,说明一下分代算法中的具体内容。
常见误区
根据《深入理解JAVA虚拟机》第三版P45页的说明,JAVA堆并非划分为“新生代”、“老年代”、“永久代”、“Eden空间”、“From Survivor空间”“To Survivor”等空间,这些划分主要是由于十年前,HotSpot虚拟机的内部垃圾回收器都是基于分代理论进行划分的,但是在G1之后出现了很多垃圾收集器采用的并非是新生代和年老代的划分,所以这样的说法在现在看来就不太正确了。
同样的关于“永久代”,永久代并不等于方法区,永久代是方法区的一种实现方式,在一些其他虚拟机中是不存在永久代概念的。并且JDK7中,HotSpot将永久代中的字符串常量池、静态变量移除,到了JDK8中完全放弃了永久代的概念,改用在本地内存中实现元空间来代替。
分代收集设计
新生代
在分代设计的JAVA堆中,新生代可以进一步分为Eden和两个存活区(Survivor space)。一个对象总是在Eden中生成,经历过回收之后,才会被复制到存活区。每次回收之后,Eden和存活区中的存活对象将被复制到另一个存活区,两个存活区交替使用。
年老代
大对象直接进入年老代。在年老代中一般采用标记清除压缩的方法来进行GC。
年龄计数器
HotSpot给每个对象定义了一个对象年龄计数器,对象在Survivor中每经历一词Minor GC,则年龄加1,当年龄增加到一定程度(默认为15)之后会被放入年老代。
动态年龄判断
但是如果Survivor空间中相同年龄的对象总大小大于Survivor空间的一半,则大于等于该年龄的所有对象直接进入年老代。
空间担保分配
一般来说,当新生代空间不足的时候需要发生Minor GC,在这之前,虚拟机必须检查年老代中最大可用连续空间是否大于新生代所有对象总空间,如果是,则说明这是一个安全的Minor GC,否则,判断是否允许担保失败,如果是,则检查年老代最大可用的连续空间是否大于历次晋升到年老代对象的平均大小,如果大于,则进行一次有“风险”的Minor GC,否则改为进行一次Full GC。
这里的风险指的是空间分配担保,如果Survivor不足以容纳一次GC之后存活的对象,则需要把这些对象直接放入年老代中。