垃圾回收

来源:互联网 发布:深圳cnc编程招聘 编辑:程序博客网 时间:2024/06/09 19:03

垃圾收集(Garbage Collection,GC)主要关注堆和方法区,回收满足回收条件的空间。

对象回收

对于堆上的对象实例,垃圾收集器在进行回收操作前,首先要判别实例对象是否被引用,准确的是判别对象是否“死亡”,因为有引用不代表不需要回收。判别对象时候被引用有2个算法:

  1. 引用计数法

给对象添加一个引用计数器,每当有一个地方引用它时,计数值就加1;当引用失效时就减1。任何时候如果计数器为0,就说明这个对象没有任何引用。

但是JAVA没有使用这种算法来判别对象的引用。这是因为要避免2个对象互相引用的情况。

A、B两个对象相互引用,但除此之外没有任何引用,从道理上来讲它们应该被回收,但是由于引用计数器不为0,无法回收。

  1. 根搜索算法

本算法通过一系列名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

JAVA中GC Roots一般有:

  • 栈帧中的本地变量表中引用的对象
  • 方法区中的类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI的引用对象

JDK 1.2后JAVA的引用开始分为强引用、软引用、弱引用、虚引用。这些在之前的文档中详细介绍过,这里就不再啰嗦啦。

当然,在根搜索算法中不可达的对象,也不是必定会被回收。宣告一个对象死亡至少要经历2次标记。

如果对象进行根搜索算法后发现没有与GC Roots的引用链,那它将会被第一次标记并进行一次筛选,筛选条件是该对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或是finalize()方法已经被虚拟机调用过,虚拟机将这两种情况视为没有必要执行。

如果对象有必要执行finalize()方法,那么它将会被放置在一个名为F-Queue的队列中,并在稍后由一条由虚拟机自动建立的、低优先级的Finalizer线程去执行(虚拟机会触发这个方法,但并不承诺会等待它运行结束)。稍后会堆队列中的对象进行第二次标记,如果在这之前对象能够重新与引用链上的任意对象关联,那么它将拯救自己,否则就真的被标记为死亡了。当然标记为死亡也不代表就一定被回收……

JAVA虚拟机规范没有要求在方法区中实现垃圾收集,且在方法区中进行垃圾回收的性价比较低。

对于HotSpot来说,方法区中主要回收两部分:废弃常量和无用类。

垃圾回收算法

  1. 标记-清除算法

首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。

缺点:

1)效率不高

2)空间问题,清除后碎块太多

  1. 复制算法

它将内存分为相同大小的2块,每次只使用其中一块,当一块内存用完时,就将存活的对象复制到另外一块上,并清空原数据块。

它对空间要求比较大,但是用于新生代的回收还是相当有效的,IBM研究表明,新生代中的对象有98%是朝生夕死的。

  1. 标记-整理算法

类似于标记-清除算法,但是它不会直接进行清除操作,而是将存活对象都向一端移动,然后清除掉端边界外的内存。

  1. 分代收集算法

根据内存的存活周期的不同将内存划为几块。一般把java堆分为新生代和老年代。由于新生代的对象每次回收都有大量的对象死去,所以用复制算法。对于老年代对象存活率高,可以采用标记-清除或是标记-整理算法。

垃圾收集器

HotSpot中有多个垃圾收集器,应对不同的情况。很多情况下垃圾收集器都需要组合使用。


原创粉丝点击