垃圾回收算法与思想

引用计数法(Reference Counting):算法

引用计数法是最经典也是最古老的的一种垃圾收集算法,大体思想是:为每个对象配备计数器,若是有引用就+1,若是引用失效就-1,若是某个对象的计数器为0,说明没有引用,则能够被回收。优化

可是引用计数法有一个显著的缺点就是没法回收循环引用的对象,例若有A和B两个对象,二者相互调用,可是这两个对象除了相互调用外,没有被其余任何一个对象调用,可是由于相互调用,计数器不为0,因此不会被回收,这就可能会致使内存泄漏。spa


标记-清除算法(Mark-Sweep):线程

标记-清除算法是现代垃圾回收算法的基础思想,标记-清除算法将垃圾回收分为两个阶段,一个是标记阶段,另一个是清楚阶段,对象

标记阶段:从根节点开始,标记全部从跟节点到可达到的对象,未标记的对象就是没有别引用的对象。生命周期

清除阶段:清除全部未被标记的对象。内存

标记-清除算法最大的问题就是空间碎片,垃圾回收以后,空间是不连续的,在对象的堆空间分配过程当中,尤为是大对象,不连续的内存空间的工做效率低于连续的。rem


复制算法(Copying)io

复制算法是一种相对比较高效的算法,主要思想是:将内存空间分为大小相等的两部分,每次只是用一块,在垃圾回收的时候,将正在是用的那一块空间中的引用对象复制到另一块中,而后清除正在使用的那一块空间的全部对象,交换两个内存的角色,完成垃圾回收。效率

优势是回收事后的内存空间没有碎片。

缺点是内存折半,复制大量的对象。

Java的新生代串行垃圾回收器用的就是这个算法。新生代分为eden,from space, to space三个部分,from space 和 to space是大小相等,地位相等,可进行角色互换的空间块。

在垃圾回收时,eden中的存活对象会被复制到未使用的survivor中(假设是to space),正在使用的存活对象也会从from space复制到to space中,eden 和 from space留下来的是未被使用的对象,被直接清空,to space存放复制事后的存活对象。


标记-压缩算法(Mark-Compact)

复制算法的高效性是创建在存活对象少,垃圾对象多的状况,如新生代。可是若是是老年代的话,复制成本太高,则就不适用了。

标记-压缩算法是一种老年代的回收算法,它是在标记-清除算法的基础上作了一些优化,如标记-清除同样,首先也是从根节点开始,对全部可达对象进行标记,可是不一样的是,标记-压缩会将存活对象压缩到内存的一端,而后清理边界外的全部空间,这样的好处是不用两块内存相同的空间和避免了空间碎片。


增量算法(Incremental Collecting)

对大部分垃圾回收算法而言,在进行垃圾回收期间,咱们的应用程序是处于挂起的状态,也称之为Stop the World状态,可是若是垃圾回收的时间过长的话,则应用程序挂起时间会好久,这样会严重影响用户体验和系统的稳定性。

增量算法的基本思想是,若是一次性将全部的垃圾进行回收,须要形成系统长时间的停顿,那么就能够想到一个折中的办法,让垃圾回收线程和应用程序线程交替运行,垃圾回收线程只回收一小部份内存,而后就切换到应用程序线程,如此反复,直到执行完垃圾回收。在垃圾回收过程当中,还执行了应用程序的代码,这样减小了系统停顿的时间,可是由于垃圾回收线程和上下文线程的切换的消耗,形成了垃圾回收成本的增长,从而致使了系统吞吐量的降低。


分代算法(Generational Collecting)

标记-清除,复制,标记-压缩等算法,在这些算法中,并无一种算法彻底代替其余算法,它们都就有各自的优点和特色。

所以,根据回收对象的特色,选择合适的回收算法,才是明智的选择。

分代就是基于这样的思想,它将内存区间根据对象的特色进行划分,而后根据各个内存区间的特色,使用不一样的垃圾回收算法,提升垃圾回收效率。

以Hot Spot为例,它将全部新建的对象都放入称为新生代的内存区域,新生代的特色是对象朝生夕灭,大约90%对象都会被回收,所以新生代就适用用高效的复制算法;通过几回垃圾回收依然存活的对象,就会保存在老年代中,老年代中几乎全部的对象都是通过几回垃圾回收依然幸存的,在一段时间内,甚至应用程序整个生命周期,都是内存常驻的。对于老年代,由于大部分对象是存活的,因此采用复制算法的话,是不可取的,采用标记-压缩算法才是高效的。

相关文章
相关标签/搜索