上一章节中,我们知道了如何去判断对象是否还存活。那么当我们知道了那些不再存活的对象时,就需要及时的把这些对象清除掉,这也就是在JVM中重要的操作---GC(garbage collection) 内存回收操作。这章节中我们主要是要去知道关于一些GC操作的算法。
在了解这些算法之前呢,需要大致的对堆中的分代简单了解一下。
在堆中,存在新生代跟老年代这么2块内存空间。一般而言:新生代所占用的内存空间相对小于老年代所占空间。在Hotspot中,新生代中又被划分成了 Eden,survivor From,survivor To这三块区域,并且占比为 8 :1 :1 ,新生代中的对象大多都是“朝存夕亡”,而老年代中的对象大多都是一直存在的,所以GC在新生代中会比老年代中频繁。
关于GC算法大致有以下这么几种:
1.标记-清除算法
如图:标记-清除算法共包括了2个阶段,标记&清除。标记需要被回收的对象,然后再统一进行清除
主要有2个不足:
1).标记跟清除操作效率不高。
2).产生浮动碎片,清除后的空间不连续,可能导致在分配大空间对象时,无法分配造成提前GC。
2.标记-复制算法
为了弥补标记-清除算法存在效率及不连续内存空间的缺陷,复制算法的出现很好的弥补了标记-清除算法中的缺陷。
复制算法中 会先把内存空间分配成1;1的两块等分空间,每次只使用其中一块空间,当其中一块内存空间满了后,就会把存活的对象复制到另一块内存空间上去,这样就不会产生内存碎片并且高效。
在Hotspot中,采用了这种算法用于新生代对象的回收,由于这个新生代对象大多都是"朝存夕亡",所以并不需要以1:1的比例进行内存空间分配,而是将内存分成Eden,survivor from ,survivor to 三块区域,并且分别以Eden:survivor from:survivor to=8;1:1的比例分配。每次都会使用Eden和其中一块survivor区域的空间,当需要进行垃圾回收的时候,就会把这两块空间中的存活对象复制到另一块survivor空间上去,然后再清除掉原来的Eden,survivor内存空间
Hotspot实现复制算法的原理:
Eden跟被用过的survivor配比默认为8:1 ,也就意味着每次新生代中的可用内存空间占整个新生代内存空间的90%,只有10%的内存会被"浪费"掉,我们并不能保证每次可回收的对象都低于90%,当在分配空间时,survivor空间不够时,会依赖于老年代的空间进行分配担保。
3.标记-整理算法
既然有个复制算法这么优秀的算法,为什么还会出现标记-整理算法呢?
对于老年代中的对象而言并不向新生代对象那么交替快,老年代中的对象大多数都是存活的,因此对于复制算法理念--复制存活的对象,这样当使用在老年代对象回收时,效率就没那么高,并且在老年代中并没有多余的空间进行分配担保。
标记-整理算法:把所有存活的对象都向一端移动,然后再清理掉边界以外的内存,从此来达到GC的目的。
4.分代收集算法
对于分代收集算法而言 顾名思义 分代分代 就是用适当的算法回收适当分代里的内存,以此充分算法的最大作用与效率
在新生代中,大多数的对象都是"朝存夕亡",那么就用复制算法,只需要付出少量对于存活对象的复制成本就可以完成GC
而对于老年代中,恰恰相反老年代中的对象存活率很高,没有其他的空间进行分配担保,那么就必须采用标记-清除或标记-整理算法。