在 JDK 1.2 之前,Java 对象的引用很传统:如果 reference 类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。这种定义很纯粹,但是太过狭隘,一个对象在这种状态下只有被引用或不被引用两种状态,有时候我们希望描述这样一类对象:当内存空间还足够时,则能保留在内存之中;如果内存空间在进行垃圾收集后还是非常紧张,则可以抛弃这些对象。
本文主要介绍几种常用的垃圾回收算法:标记 - 清除算法、复制算法、标记 - 整理算法、分代收集算法。
关于 Java 的垃圾收集( Garbage Collection,GC ),Java 运行时区域的各个部分,程序计数器、虚拟机栈、本地方法栈 3 个区域随线程而生,随线程而灭;栈中的栈帧随着方法的进入和退出而有条不絮的执行者岀栈和入栈操作。
每个栈帧中分配多少内存基本上实在类结构确定下来时就已知的,因此在这几个区域内存的分配和回收都具有确定性,就不需要过多考虑回收的问题,因为在方法结束或者线程结束时,内存就自然跟着回收了。既然 Java 的内存回收已经如此智能,我们为什么还有继续了解 GC 和内存分配呢?
在可达性分析算法中不可达的对象,也并非是“非死不可”的,一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没有与 GC Roots 相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize ( )
方法。当对象没有覆盖 finalize ( )
方法,或者 finalize ( )
方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。
很多人认为方法区(或者 HotSpot 虚拟机中的永久代)是没有垃圾收集的,Java虚拟机规范中确实说过可以不要求虚拟机在方法区实现垃圾收集,而且在方法区中进行垃圾收集的“性价比”一般比较低:在堆中,尤其是在新生代中,常规应用进行一次垃圾收集一般可以回收 70%~95% 的空间,而永久代的垃圾收集效率远低于此。
Java 虚拟机规范中对垃圾收集器应该如何实现并没有任何规定,因此不同的厂商、不同版本的虚拟机所提供的垃圾收集器都可能会有很大差别,并且一般都会提供参数供用户根据自己的应用特点和要求组合出各个年代所使用的收集器。下面是 JDK 1.7 Update 14之后的 HotSpot 虚拟机包含的所有收集器: