垃圾回收算法
标记清除算法
就是根据之前的可达性分析算法+四种引用类型对象判断,来标记那些是可以被回收的对象(垃圾对象),哪些是存活的对象,然后对垃圾对象进行清理回收
如果我们仅仅是采用标记-清除算法,标记哪些对象是可以回收的,那些对象是不可以回收的,然后针对可回收的内容进行回收,就会导致一个不好的后果,产生大量的内存碎片
内存碎片
内存碎片一般是由于空闲的连续空间比要申请的空间小,导致这些小内存快不能被利用
标记-复制算法
半区复制
简单来说,将新生代的内存分为大小相等的两块,每次只是用其中的一块,当这块空间用完的时候,就将还存活的对象复制一份到另一块上面去,然后再把已经使用的内存空间一次性清理掉
因此JVm也采用了复制算法,当真正发生垃圾回收的时候,JVM会将第一块空间中的那些对象是可以回收的,不能回收的进行标记[标记清除算法],然后将不可回收的对象统统复制到下面那块内存区域中,并且复制的时候可以紧凑的排列一起,最大化利用内存空间
复制算法的缺点
复制算法确实可以解决内存碎片的问题,也使得我们的回收工作更效率,但是缺点很明显,就是将原来可以使用的内存空间减少为原来的一半,空间浪费有点太多了
IBM公司曾有一项目专门研究对新生代"朝生夕死"的特点做了更量化的诠释--新生代中的对象98%都熬不过第一轮收集,因此并不需要按照1:1的比例来进行划分新生代的内存空间
半区复制分代策略
现在称为appel式回收,HotSpot虚拟机的Serial,ParNew等新生代收集器均采用了这种策略来设计新生代的内存布局
Appel式回收的具体做法是把新生代分为一块较大的Eden空间和两块较小的Survivor空间,每次分配内存只是用Eden和其中一块Survivor.发生垃圾收集时,将Eden和Survivor中仍然存活的对象一次性赋值到另一块Survivor空间上,然后直接清理掉Eden和Survivor空间
HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是说每次新生代中可用内存空间为整个新生代容量的90%,只有一个Survivor空间是会被“浪费”的
当然98%的对象可被回收仅仅是"普通场景"下测的的数据,任何人都没有办法百分百保证每次回收都只有不多于10%的对象存活,因此Appel式回收还有一个充当罕见情况的"逃生门"的安全设计,当Survivor空间不足以容纳一次Minor GC之后存活的对象时,就需要依赖其他内存区域(实际上大多就是老年代)进行分配担保(Handle Promotion)
分配担保
简单来说,当剩下的Survivor空间不足以容纳上一次新生代收集下来的存活对象时,这些对象就会通过分配担保机制直接进入到老年代,这对虚拟机来说是安全的