JVM垃圾收集

垃圾收集器的历史比Java久远,1960年诞生于MIT的Lisp是第一门真正使用内存动态分配和垃圾收集技术的语言。

对于垃圾回收,我们首先考虑的是什么样的对象为可回收对象,即使对象什么时候判定为死亡,其次是什么时候进行回收,最后是如何回收。

1.什么样的对象为可回收对象?

当对象死亡,JVM便对其分配的内存进行回收,对其进行销毁。

⑴如何判断对象死亡,即什么才算对象死亡?

引用计数算法

给对象添加一个引用计数器,当对象被引用的时候,计数器就加一,当引用失效的时候,计数器的值就减一,当计数器的值为0时,这个对象便不在被使用便可以被GC进行回收。

引用计数算法,简单,高效,判定效率也很高,也是一个不错的算法,但至少主流的Java虚拟机没有选用引用计数法来管理内存,其主要的原因是它很难解决对象之间相互循环引用问题,ex:objA.instance = objb及objB.instance =objA,这两个对象已再无其他引用,实际上这两个对象已经不可能在被访问,但是因为他们互相引用着对方,导致引用计数器的计数值不为0,所以GC收集器无法回收们。

可达性分析算法

在主流的程序语言的主流实现中,都是称通过可达性分析算法来判断对象是否存活。可达性分析算法,就是通过一个称为“GC Roots”的对象作为起始点,从这些节点开始往下搜索,搜索所走过的路径称为引用链,当一个对象到达GC Roots没有任何引用链相连的时候,也就是GC Roots到这个对象不可达,则证明此对象不可用。

可作为GC  Roots对象包括:虚拟机栈中引用的对象,方法区中类的静态属性引用的对象,方法区中常量引用的对象,Native引用过的对象。

引用的分类

在JDK1.2之后,Java对引用的概念进行了扩充,将引用分为强引用,软引用,弱引用,虚引用

强引用:也就是使用new关键字创建的对象,只要引用还在,垃圾收集器就永远不会回收掉被引用的对象。

软引用:引用一些非必需的对象,对于软引用的对象,当系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行二次回收。如果回收后还没有足够的内存,才会抛出异常。JDK1.2之后,提供SoftReference类来实现软引用。 

弱引用:被弱引用关联的对象只能生存到下一次垃圾收集发生之前,当垃圾收集器工作时,无论当前内存是否足够,都会回收掉被弱引用关联的对象。在JDK1.2之后,提供WeakReference类来实现弱引用。

虚引用:最弱的一种,一个对象是否有虚引用的存在,完全不会对其生存期间构成影响,也无法通过虚引用来取得一个对象实例。在JDK1.2之后,提供了PhantomReference类来实现。

2.什么时候进行回收?

当对象的引用计数为0或者在可达性分析算法中不可达时,就进行回收。

即使在可达性分析算法中不可达的对象,也并非非死不可,要真正宣告一个对象的死亡,需要经历两次标记过程。

当对象在进行可达性分析算法中,没有与GC Roots相连接,则会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者虚拟机没有调用过此方法,虚拟机都将视为“没有必要执行”。如果对象被判定为有必要执行,他将会被放置到一个F-Queue队列中,队列是一个先进先出的数据结构。Finalize()方法是对象逃脱死亡的最后一个机会,所以要想拯救,只需重新与引用链上的任何一个对象建立关联即可。

3.如何回收?垃圾收集算法有哪些

⑴标记-清除算法

首先标记出所有需要回收的对象,标记结束后再进行统一的回收。缺点:产生大量的内存碎片,效率不是太高

⑵复制算法

将内存划分为大小相等的两部分,每次只使用其中的一个,当这一块用完了,就将还存活的对象复制到另外一块上面,然后把已使用过那块的内存空间一次清理掉。优点:简单,高效。

⑶标记-整理算法

与标记-清除算法一样,但后续步骤不是直接进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

⑷分代收集算法

根据对象的存活周期的不同将内存划分为几块。一般把Java堆划分为新生代和老年代,根据不同的各个年代的特点采用最适当的收集算法。在新生代中,一般采用复制算法,因为新生代对象活动频繁,生死操作快。而在老年代因为对象存活率高,所以就必须使用标记-清理或者标记-整理来进行回收。

4.回收方法区

很多人认为方法区(在HopSpot虚拟机中的永久代)中没有垃圾收集的,Java虚拟机规范中说过可以不要求。在方法区中进行垃圾回收的性价比一般比较低。

永久代的垃圾收集只要是两部分:废弃常量和无用的类。

废弃常量:没有任何对象引用常量池中的次常量,也没有其他地方引用这个字面量。

无用的类:该类的所有的实例都已经被回收,即Java堆中不存在该类的任何实例;加载该类的ClassLoader已经被回收;该类对应的java.lang.Class对象没有在任何地方被引用,也无法通过反射来访问该类。

 

本章节介绍了对象死亡的判定,以及如何进行回收,以及垃圾收集的算法,内容参考来自《深入理解Java虚拟机》,下一章节介绍各个垃圾收集器。如有错误,敬请指出,谢谢