这篇文章主要讲讲jvm有关垃圾收集的知识,本篇文章分为三个部分,虚拟机什么时候回收对象、垃圾收集算法与垃圾收集器
一、对象什么时候回收
对象什么时候回收,换句话说就是虚拟机如何判断对象是该“生存”还是“死亡”。
1、引用计数算法
引用计数算法是一个非常高效的算法,用途也非常光,其中非常著名的就是微软公司COM (Component Object Model)技术。它的主要思想就是给对象添加一个引用计数器,当有一个地方引用这个对象时引用计数器就加1,当失去这个引用后,引用计数器就减1。直到虚拟机要进行垃圾回收的时候,就会把计数器为零的对象清理掉。
此算法虽好,但是它并没有运用到java虚拟机里面,其中最主要的问题就是他没法处理对象之间相互循环引用的问题。
所谓对象循环引用,举个例子,比如说,一个类声明了一个属性 a ,这个类有两个不同的实例对象obja,objb,此时,使obja.a=objb, objb.a=obja,实际上这两个对象已经不可能再被访问了,但是还互相引用着对方,如果采用引用计数算法,则虚拟机无法把他们两个回收。
2、可达性分析算法
此算法为java通用的算法。它的主要思想是:通过一系列“GC Root”节点,由上至下开始搜索,走过的路径被称为“引用链”,当检测到有对象并没有在这条链上的时候,也就是说,这个对象不可达的时候,就可以说这个对象不可用了,可以被回收了。
在java中可以被用作GC Root的对象有:
虚拟机栈(帧栈的本地变量表)中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法中JIN(即一般说的Native方法)引用的对象
二、垃圾收集算法
垃圾收集算法指的是虚拟机当发现有可以回收的对象之后如何去回收的方法,主要有三种,标记--清除算法,复制算法,标记--整理算法。
1、标记--清除算法
这个算法是最基础的算法,理解起来也比较简单。主要就是分为两个阶段:标记和清除。首先,虚拟机会标记处所有需要回收的是对象,标记之后统一回收被标记的对象。
这个算法也有不足的地方,其一,效率不是很高,两步的效率都很低。其二,当把标记的对象清除以后,很容易产生大的内存碎片,内存空间不连续,当有大对象进来时,因为找不到足够的空间而不得不触发下一次的垃圾收集。
2、复制算法
复制算法很好的解决了效率的问题,它的思想是:将内存划分为两个部分,每次只是用其中的一块,当这一块用完了,就会把这块上还存活的对象都复制到另一块内存上,然后清理掉这块内存。只不过代价也很大,只有一半的内存空间可以使用。
这个算法普遍应用在堆的新生代回收中,新生代中分为Eden、Survivor区,但是他们并不是1:1比例的,一般默认为8:1,此比例可以用修改。在新生代中大部分对象都是存活时间不长的,所以每次垃圾回收后,存活下来的就没有多少了。每个新生代中,Eden只有一块,但是survivor有两块,每次使用Eden和一块survivor区,当垃圾回收时,会将存活下来的对象复制到另外一块survivor中,当不够用时会依赖老年代进行分配担保。
3、标记--整理算法
此算法的标记过程与标记--清除算法的一样,只不过会将标记出的存活的对象全都整理到一边,将另一边的部分全都清理掉。
此算法普遍运用在老年代中。
4、分代收集算法
就是将老年代与新生代运用不同的算法进行垃圾收集。