清理:终结处理和垃圾回收
1、finalize()方法
在Java中垃圾回收器负责回收无用对象占据的内存资源,它只知道释放那些经由new分配的内存。
在编程中,存在通过某种创建对象方式以外的方式为对象分配了存储空间的情况(这种情况主要发生在使用“本地方法”的情况下,本地方法是一种在Java中调用非Java代码的方式,例如调用了C的malloc()函数,此时需要调用free()函数才可以释放空间,所以可以在finalize()方法中用本地方法调用它),这时候垃圾回收器就无法释放该对象的这块“特殊”内存。因此可以通过在类中定义一个名为finalize()的方法来做一些清理工作,它的工作原理:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。
在Java中,无用对象并不总是被垃圾回收,因此finalize()方法并不是一定会被调用,与C++的析构函数不同。如果程序执行结束,并且垃圾回收器一直都没有释放你创建的任何对象的存储空间,则随着程序的退出,那些资源也会全部交还给操作系统。
finalize()方法的要点:
①对象可能不被垃圾回收
②垃圾回收并不等于“析构”
③垃圾回收只与内存有关
2、终结条件
finalize()方法并不是一个可以依赖的用于清理的方法,但它可以用于找出缺陷。例如终结调教是:在程序结束前,应当对所有的书进行录入。这时可以在程序结束时垃圾回收器工作前调用finalize()方法来判断是否存在没有录入的书本,代码如下所示。
1 public class TerminationCondition { 2 public static void main(String[] args) { 3 Book novel = new Book(true); 4 novel.checkIn(); 5 new Book(true); 6 System.gc();//强制进行终结动作 7 } 8 } 9 10 class Book{ 11 boolean checkedOut = false; 12 Book(boolean checkedOut){ 13 this.checkedOut = checkedOut; 14 } 15 void checkIn(){ 16 checkedOut = false; 17 } 18 protected void finalize(){ 19 if(checkedOut){ 20 System.out.println("Error:checked out"); 21 } 22 } 23 }
3、垃圾回收器工作原理
垃圾回收器对于提高对象的创建速度具有明显的效果。垃圾回收器工作时,一面回收空间,一面使堆中的对象紧凑排列,这样“堆指针”就可以很容易移动到更靠近传送带的开始出,也就尽量避免了页面错误。Java的“堆指针”只是简单地移动到尚未分配的区域,其效率比得上C++在堆栈上分配空间的效率。
垃圾回收技术:
①引用计数:每个对象都含有一个引用计数器。当有引用连接至对象时,引用计数加1;当引用离开作用域或被置为null时,引用计数减1。垃圾回收器会在含有全部对象的列表上遍历,当发现某个对象的引用计数为0时,就释放其占用的空间。缺点:无法检测出循环引用。如父对象有一个对子对象的引用,子对象反过来引用父对象。这样,他们的引用计数永远不可能为0。
②停止—复制:先暂停程序的运行(所以它不属于后台回收模式),然后将所有存活的对象从当前堆复制到另一个堆,没有被复制的全部是垃圾。在新堆中保持紧凑排列,对对象的所有引用都必须进行修正。
③标记—清扫:从堆栈和静态存储区出发,遍历所有引用,进而找出所有存活的对象。每当找到一个存活对象就设一个标记。全部标记工作完成后,将没有标记的对象释放,这使得堆空间不连续。
Java虚拟机会使用②和③组成的一种自适应的垃圾回收技术。如果所有对象很稳定,垃圾回收器效率降低的话,就采用“标记—清扫”方式;如果堆空间出现很多碎片,就采用“停止—复制”方式。
参考于《Java编程思想》,第87~91页