垃圾收集器对堆进行回收前,会先确定对象是在存活还是已经死去
下面是俩种判定对象是否存活的算法.
对象存活算法
引用计数算法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能在被使用的.此算法的问题是无法解决对象之间循环引用的问题.
根搜索算法
主流的编程语言都是使用根搜索算法(GC Roots Tracing)判定对象是否存活的.这个算法的思路是通过一系列名为GC Roots的对象作为起始点,从这些节点开始向下搜素,搜索锁走过的路径成为引用链(Refernce Chain),当一个对象到GC Roots没有任何引用链相连时则此对象是不可用的.
GC Roots对象包括下面几种:
Ø 虚拟机栈(栈帧中的本地变量表)中引用的对象
Ø 方法区中的类静态属性引用的对象
Ø 方法区中的常量引用的对象
Ø 本地方法栈中JNI(native)的引用对象
垃圾回收进行时会从这些GC Roots开始查找被引用的对象,这样会形成一颗颗以GC Roots为根的对象树,在这些对象树上的对象都是当前系统运行所需要的对象,不能被垃圾回收.
垃圾收集算法:
标记-清除(Mark-Sweep)
垃圾收集基础算法,分为俩个阶段:首先根据根搜索算法标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象.
缺点:此算法会暂停整个应用,会产生内存碎片.
复制(copying)
此算法将可用内存按容量分为大小相等的俩块,每次只使用其中的一块,当着一块的内存用完了,将还存活着的对象复制到另外一块,然后把已使用过的内存空间一次清理掉.
优点:不会出现碎片的问题
缺点:需要俩倍的内存, 在对象存活率较高时要执行较多的复制操作,效率会变低,并且需要额外的空间进行分配担保.
商业虚拟机采用这种复制收集算法来回收新生代,新生代中的对象98%是朝生夕死的,所以不需要安装1:1的比例来划分内存,而是将内存划分为一块较大的Eden和俩个较小的Survivor空间,每次使用Eden和其中的一个Survivor,当回收时将Eden和Survivor中还存活的对象一次性地拷贝到另外一个Survivor空间,最后清理掉Eden和刚才的Survivor.HotSpot默认Eden和Survivor的大小比例是8:1.
标记-整理(Mark-Compact)
分俩阶段:第一阶段是从根节点开始标记所有被引用对象,第二个阶段遍历了整个堆,让所有存活的对象都向一端移动,最后直接清理掉边界以外的内存.
分代收集算法
根据对象的存活周期的不同将内存划分为几块,一般是把java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法,在新生代中每次收集都有大批对象死去,只有少量存活,采用复制算法.老年代中因为对象存活率高,没有额外的空间进行分配担保,必须使用标记-清理或标记-整理算法.