垃圾收集
9.2 垃圾收集算法
任何被根对象引用的对象都是可触及的,从而是活动的,另外,任何被活动的对象引用的对象都是可触及的,程序可以访问任何可触及的对象,所以这些对象必须保留在堆里面,任何不可触及的对象都可以被收集,因为程序没有办法来访问它们。
9.3 引用计数收集器
堆中每一个对象都有一个引用计数,当一个对象被创建,并且指向对象的引用被分配给一个变量,这个对象的引用计数被置为1,当一个对象的引用超过了生存期或者被设置一个新的值时,对象的引用计数减1,这个方法中,一个对象被垃圾收集后可能导致后续其他对象的垃圾收集行动。这种技术现在已经不为人接受。
9.4 跟踪收集器
跟踪收集器追踪从根节点开始的对象引用图,在追踪过程中遇到的对象以某种方式打上标记,当追踪结束时,未被标记的对象就知道是无法触及的,从而可以被收集。
基本的追踪算法被称为“标记并清除”,他指出垃圾收集过程的两个阶段,在标记阶段,垃圾收集器遍历引用树,标记每一个遇到的对象,在清除阶段,未被标记的对象被释放,使用的内存被返回到正在执行的程序,在JAVA虚拟机中,清除步骤必须包括对象的终结。
9.5 压缩收集器
标记并清除收集器使用的两种策略是压缩和拷贝,这两种方法都是快速移动对象来减少堆碎块,压缩收集器把活动的对象越过空闲区滑动到堆的一端,在这一过程中,堆的另一端出现一个大的连续空闲区,对象的引用实际指向一个对象句柄表,对象句柄才指向堆中对象的实际位置。当对象被移动了,只有这个句柄需要被更新为新位置。
9.6 拷贝收集
拷贝垃圾收集器把所有的活动对象移动到一个新的区域。拷贝的过程中,它们被紧挨着布置,所以可以消除原来它们在旧区域的空隙。原来的区域被认为是空闲区。
9.7 按代收集的收集器
通过把对象按照寿命来分组解决了效率地下问题,更多的收集那些短暂出现的年幼对象,而非寿命较长的对象。堆被划分成两个或者更多的子堆,每一个子堆为一代对象服务。最年幼的那一代进行最频繁的垃圾收集。
按代进行的收集技术除了可以应用拷贝算法,亦可以应用于标记并清除算法,不管哪一种情况,把堆按照对象年龄层分解都可以提高最基本得垃圾算法的性能。
9.8 自适应收集器
自适应算法监视堆中的情形,并且对应调整为合适的垃圾收集技术。
9.9 火车算法
每一次火车算法被执行时候,它要么收集最小数字火车中的最小数字车厢,要么收集整列最小数字火车,算法首先检查指向最小数字火车中任何车厢的引用,如果不存在任何来自最小数字火车以外的引用指向它内部包含的对象,那么整列最小数字火车包含的都是垃圾,可以被抛弃。算法首先把所有被最小数字车厢外部的车厢引用的对象转移到其他车厢。当进行这个移动后,任何保留在车厢内的对象都是没有引用,可以被垃圾收集。
因为火车算法在检查最小数字车厢之前,首先检查最小数字火车是否完全就是垃圾,他可以收集任何大小的循环数据结构。
记忆集合和流行对象:为了促进收集过程,火车算法使用记忆集合。一个记忆集合是一个数据结构,它包含了所有对一节车厢或者一列火车的外部引用。一个空的记忆集合显示车厢或者火车中的对象都不再被车厢或者火车外的任何变量引用(遗忘),被遗忘的对象就是不可触及的可以被垃圾收集。
9.10 终结
在java语言里,一个对象可以拥有终结方法:这个方法是垃圾收集器在释放对象前必须运行。
class Example {
protected void finalize() throws Throwable{
super.finalize();
}
}
垃圾收集器必须检查它所发现的不再被引用的对象是否存在finalize()方法。首先,垃圾收集器必须使用某种方法检测出不再被引用的对象(称作第一遍扫描),然后,它必须检查它检测出的不再被引用的对象是否声明了终结方法。当执行所有的终结方法之后,垃圾收集器必须从根节点开始再次检测不再被引用的对象(称作第二遍扫描),这个步骤是必要的,因为终结方法可能“复活”某些不再被引用的对象,使它们再次被引用。最后,垃圾收集器才能释放那些在第一次和第二次扫描中发现的都没有被引用的对象。使用java编程时,必须记住一点:是垃圾收集器运行对象的终结方法,因为总是无法预测何时对象被垃圾收集,所以也无法预测对象的终结方法何时运行。
9.11 对象可触及性的生命周期
堆中每一个对象都有三种 状态之一:可触及的,可复活的,以及不可触及的,如果垃圾收集器可以从根节点开始通过追踪“触及”到这个对象,它就是可触及的每一个对象都是从可触及状态开始它的生命周期的,只要程序还保留至少一个可以触及的引用到该对象,它就是一直保持可触及状态。一旦程序释放了所有到该对象的引用,然后这个对象就变成了可复活状态,在执行所有可复活对象可能声明的finalize()方法之后,垃圾收集器会把那些处于可复活状态的对象或者转化为可触及的状态,或者前进到不可触及状态。
不可触及状态标志着不但对象不再被触及,而且也不可能通过任何终结方法复活,不可触及的对象不再对程序的执行产生影响,可以自由的回收它们所占据的内存。
9.11.1 引用对象
所有的引用对象都是抽象的java.lang.ref.Reference类的子类的实例,包含了三个直接子类:SoftReference(软引用),WeakReference(弱引用),PhantomReference(影子引用),强引用禁止引用目标被垃圾收集,而软引用,弱引用和影子引用不禁止,要创建一个软引用,弱引用或者影子引用,简单的把强引用传递到对应的引用对象的构造方法中去。
要清除一个引用对象,程序或者垃圾收集器只需要调用引用对象的clear()方法,通过清除引用对象就切断了引用对象的软引用,弱引用或者影子引用。
9.11.2 可触及性状态的变化
要把一个引用对象和一个引用队列关联起来,可以简单的在创建引用对象时把引用作为构造方法参数传递到引用队列。如此创建的引用对象除了保持对引用目标的引用外,还保持对引用队列的引用。当垃圾收集器对引用目标的可触及性状态做了改变时,它就会把引用对象加入到与它有关联的引用队列中。