为清除一个对象那个对象的用户必须在希望进行清除的地点调用一个清除方法这听起来似乎很容易做到但却与C++破坏器的概念稍有抵触在C++中所有对象都会破坏(清除)或者换句话说所有对象都应该破坏若将C++对象创建成一个本地对象比如在堆栈中创建(在Java中是不可能的)那么清除或破坏工作就会在结束花括号所代表的创建这个对象的作用域的末尾进行若对象是用new创建的(类似于Java)那么当程序员调用C++的delete命令时(Java没有这个命令)就会调用相应的破坏器若程序员忘记了那么永远不会调用破坏器我们最终得到的将是一个内存漏洞另外还包括对象的其他部分永远不会得到清除 相反Java不允许我们创建本地(局部)对象——无论如何都要使用new但在Java中没有delete命令来释放对象因为垃圾收集器会帮助我们自动释放存储空间所以如果站在比较简化的立场我们可以说正是由于存在垃圾收集机制所以Java没有破坏器然而随着以后学习的深入就会知道垃圾收集器的存在并不能完全消除对破坏器的需要或者说不能消除对破坏器代表的那种机制的需要(而且绝对不能直接调用finalize()所以应尽量避免用它)若希望执行除释放存储空间之外的其他某种形式的清除工作仍然必须调用Java中的一个方法它等价于C++的破坏器只是没后者方便 finalize()最有用处的地方之一是观察垃圾收集的过程下面这个例子向大家展示了垃圾收集所经历的过程并对前面的陈述进行了总结 //: Garbagejava // Demonstration of the garbage // collector and finalization class Chair { static boolean gcrun = false; static boolean f = false; static int created = ; static int finalized = ; int i; Chair() { i = ++created; if(created == ) Systemoutprintln(Created ); } protected void finalize() { if(!gcrun) { gcrun = true; Systemoutprintln( Beginning to finalize after + created + Chairs have been created); } if(i == ) { Systemoutprintln( Finalizing Chair # + Setting flag to stop Chair creation); f = true; } finalized++; if(finalized >= created) Systemoutprintln( All + finalized + finalized); } } public class Garbage { public static void main(String[] args) { if(argslength == ) { Systemerrprintln(Usage: \n + java Garbage before\nor:\n + java Garbage after); return; } while(!Chairf) { new Chair(); new String(To take up space); } Systemoutprintln( After all Chairs have been created:\n + total created = + Chaircreated + total finalized = + Chairfinalized); if(args[]equals(before)) { Systemoutprintln(gc():); Systemgc(); Systemoutprintln(runFinalization():); SystemrunFinalization(); } Systemoutprintln(bye!); if(args[]equals(after)) SystemrunFinalizersOnExit(true); } } ///:~ 上面这个程序创建了许多Chair对象而且在垃圾收集器开始运行后的某些时候程序会停止创建Chair由于垃圾收集器可能在任何时间运行所以我们不能准确知道它在何时启动因此程序用一个名为gcrun的标记来指出垃圾收集器是否已经开始运行利用第二个标记fChair可告诉main()它应停止对象的生成这两个标记都是在finalize()内部设置的它调用于垃圾收集期间 另两个static变量——created以及finalized——分别用于跟蹤已创建的对象数量以及垃圾收集器已进行完收尾工作的对象数量最后每个Chair都有它自己的(非static)int i所以能跟蹤了解它具体的编号是多少编号为的Chair进行完收尾工作后标记会设为true最终结束Chair对象的创建过程 所有这些都在main()的内部进行——在下面这个循环里 while(!Chairf) { new Chair(); new String(To take up space); } 大家可能会疑惑这个循环什么时候会停下来因为内部没有任何改变Chairf值的语句然而finalize()进程会改变这个值直至最终对编号的对象进行收尾处理 每次循环过程中创建的String对象只是属于额外的垃圾用于吸引垃圾收集器——一旦垃圾收集器对可用内存的容量感到紧张不安就会开始关注它 运行这个程序的时候提供了一个命令行自变量before或者after其中before自变量会调用Systemgc()方法(强制执行垃圾收集器)同时还会调用SystemrunFinalization()方法以便进行收尾工作这些方法都可在Java 中使用但通过使用after自变量而调用的runFinalizersOnExit()方法却只有Java 及后续版本提供了对它的支持(注释③)注意可在程序执行的任何时候调用这个方法而且收尾程序的执行与垃圾收集器是否运行是无关的 ③不幸的是Java 采用的垃圾收集器方案永远不能正确地调用finalize()因此finalize()方法(特别是那些用于关闭文件的)事实上经常都不会得到调用现在有些文章声称所有收尾模块都会在程序退出的时候得到调用——即使到程序中止的时候垃圾收集器仍未针对那些对象采取行动这并不是真实的情况所以我们根本不能指望finalize()能为所有对象而调用特别地finalize()在Java 里几乎毫无用处 前面的程序向我们揭示出在Java 中收尾模块肯定会运行这一许诺已成为现实——但前提是我们明确地强制它采取这一操作若使用一个不是before或after的自变量(如none)那么两个收尾工作都不会进行而且我们会得到象下面这样的输出 Created Created Beginning to finalize after Chairs have been created Finalizing Chair # Setting flag to stop Chair creation After all Chairs have been created: total created = total finalized = bye! 因此到程序结束的时候并非所有收尾模块都会得到调用为强制进行收尾工作可先调用Systemgc()再调用SystemrunFinalization()这样可清除到目前为止没有使用的所有对象这样做一个稍显奇怪的地方是在调用runFinalization()之前调用gc()这看起来似乎与Sun公司的文档说明有些抵触它宣称首先运行收尾模块再释放存储空间然而若在这里首先调用runFinalization()再调用gc()收尾模块根本不会执行 针对所有对象Java 有时之所以会默认为跳过收尾工作是由于它认为这样做的开销太大不管用哪种方法强制进行垃圾收集都可能注意到比没有额外收尾工作时较长的时间延迟 |