计算机杂谈

垃圾回收算法

2017-10-14  本文已影响32人  初七123

1960年诞生于MIT的Lisp语言是第一门真正使用内存动态分配和垃圾收集技术的语言。当Lisp还在胚胎时期时,人们就在思考GC需要完成的3件事情:

引用计数

给对象添加一个引用计数,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减一;任何计数器为0的对象就是不可能被使用的。客观来说引用计数算法是一个还不错的算法,它实现简单,也有一些广泛的应用,例如微软公司的COM、Python语言等。但是在主流的Java虚拟机中并没有使用它,因为它难以解决对象互相引用的问题。

可达性分析算法

在主流的商用程序语言的实现中,都是通过可达性分析来判断对象是否存活的。通过一系列成为"GC Roots"的对象为起始点向下搜索。当一个对象无法通过此路径找到则为可回收对象。

在Java语言中,可作为GC Roots的对象:

宣告死亡

即使在可达性分析算法中不可达的对象,也并非“非死不可”的。要真正宣告一个对象的死亡,至少要经过两次标记过程:如果对象在进行可达性分析后被判定死亡,那么它会执行finalize()方法并做标记同;当对象第二次被判定死亡则不会执行finalize()方法而被放到F-Queue队列中准备回收。

垃圾回收算法

Mark-Sweep(标记-清除)算法

首先标记需要回收的对象(两次可达性分析),然后统一回收。这种算法由两个缺点:一、效率不高,逐个标记和清除都很浪费时间;二、回收造成大量的内存碎片。

Mark-Sweep

Copying(复制)算法

为了解决效率问题,“复制 Copying”算法出现了。他将可用内存分为等大小的两块,每次只是用其中的一块。当一块内存用完了,就将还存活着的对象复制到另一块上面。然后再把已经使用过的内存统一清理掉(这样快很多)。但是此算法非常浪费内存!

Copying

现在的商业虚拟机都采用这种算法来回收新生代对象(存活周期短),IBM公司的研究表明新生代的对象98%都是“朝生夕死”,所以可以按照8:1来划分两块内存空间。这样就可以减少内存的浪费。

Mark-Compact(标记-整理)算法

根据老年代对象的特点(生存周期很长),有人提出了另一种“Mark-Compact”算法。首先标记要清理的对象,然后让存活的对象统一向一端移动,再统一清理掉死亡的对象。这种算法速度慢但是没有内存碎片。

Mark-Compact

分代收集算法

当前商业虚拟机(如JavaScript V8,Java)都采用分代收集算法。根据对象的存活周期分为新生代和老生代,对于新生代使用 Copying 算法提高效率,而对于老生代用 Mark-Sweep 或者 Mark-Compact 来处理。

参考

《深入理解Java虚拟机》

上一篇下一篇

猜你喜欢

热点阅读