必需执行排除
为排除一个工具,谁人工具的用户必需在但愿举办排除的所在挪用一个排除要领。这听起来好像很容易做到,但却与C++“粉碎器”的观念稍有抵触。在C++中,所有工具城市粉碎(排除)。可能换句话说,所有工具都“应该”粉碎。若将C++工具建设成一个当地工具,好比在仓库中建设(在Java中是不行能的),那么排除或粉碎事情就会在“竣事花括号”所代表的、建设这个工具的浸染域的末端举办。若工具是用new建设的(雷同于Java),那么当措施员挪用C++的delete呼吁时(Java没有这个呼吁),就会挪用相应的粉碎器。若措施员健忘了,那么永远不会挪用粉碎器,我们最终获得的将是一个内存“裂痕”,别的还包罗工具的其他部门永远不会获得排除。
相反,Java不答允我们建设当地(局部)工具——无论如何都要利用new。但在Java中,没有“delete”呼吁来释放工具,因为垃圾收集器会辅佐我们自动释放存储空间。所以假如站在较量简化的态度,我们可以说正是由于存在垃圾收集机制,所以Java没有粉碎器。然而,跟着今后进修的深入,就会知道垃圾收集器的存在并不能完全消除对粉碎器的需要,可能说不能消除对粉碎器代表的那种机制的需要(并且绝对不能直接挪用finalize(),所以应只管制止用它)。若但愿执行除释放存储空间之外的其他某种形式的排除事情,仍然必需挪用Java中的一个要领。它等价于C++的粉碎器,只是没后者利便。
finalize()最有用处的处所之一是调查垃圾收集的进程。下面这个例子向各人展示了垃圾收集所经验的进程,并对前面的告诉举办了总结。
//: Garbage.java // Demonstration of the garbage // collector and finalization class Chair { static boolean gcrun = false; static boolean f = false; static int created = 0; static int finalized = 0; int i; Chair() { i = ++created; if(created == 47) System.out.println("Created 47"); } protected void finalize() { if(!gcrun) { gcrun = true; System.out.println( "Beginning to finalize after " + created + " Chairs have been created"); } if(i == 47) { System.out.println( "Finalizing Chair #47, " + "Setting flag to stop Chair creation"); f = true; } finalized++; if(finalized >= created) System.out.println( "All " + finalized + " finalized"); } } public class Garbage { public static void main(String[] args) { if(args.length == 0) { System.err.println("Usage: \n" + "java Garbage before\n or:\n" + "java Garbage after"); return; } while(!Chair.f) { new Chair(); new String("To take up space"); } System.out.println( "After all Chairs have been created:\n" + "total created = " + Chair.created + ", total finalized = " + Chair.finalized); if(args[0].equals("before")) { System.out.println("gc():"); System.gc(); System.out.println("runFinalization():"); System.runFinalization(); } System.out.println("bye!"); if(args[0].equals("after")) System.runFinalizersOnExit(true); } } ///:~
上面这个措施建设了很多Chair工具,并且在垃圾收集器开始运行后的某些时候,措施会遏制建设Chair。由于垃圾收集器大概在任何时间运行,所以我们不能精确知道它在何时启动。因此,措施用一个名为gcrun的标志来指出垃圾收集器是否已经开始运行。操作第二个标志f,Chair可汇报main()它应遏制工具的生成。这两个标志都是在finalize()内部配置的,它挪用于垃圾收集期间。
另两个static变量——created以及finalized——别离用于跟踪已建设的工具数量以及垃圾收集器已举办完收尾事情的工具数量。最后,每个Chair都有它本身的(非static)int i,所以能跟踪相识它详细的编号是几多。编号为47的Chair举办完收尾事情后,标志会设为true,最终竣事Chair工具的建设进程。
所有这些都在main()的内部举办——在下面这个轮回里:
while(!Chair.f) {
new Chair();
new String("To take up space");
}
各人大概会迷惑这个轮回什么时候会停下来,因为内部没有任何改变Chair.f值的语句。然而,finalize()历程会改变这个值,直至最终对编号47的工具举办收尾处理惩罚。
每次轮回进程中建设的String工具只是属于特另外垃圾,用于吸引垃圾收集器——一旦垃圾收集器对可用内存的容量感想“告急不安”,就会开始存眷它。
运行这个措施的时候,提供了一个呼吁行自变量“before”可能“after”。个中,“before”自变量会挪用System.gc()要领(强制执行垃圾收集器),同时还会挪用System.runFinalization()要领,以便举办收尾事情。这些要领都可在Java 1.0中利用,但通过利用“after”自变量而挪用的runFinalizersOnExit()要领却只有Java 1.1及后续版本提供了对它的支持(注释③)。留意可在措施执行的任何时候挪用这个要领,并且收尾措施的执行与垃圾收集器是否运行是无关的。
③:不幸的是,Java 1.0回收的垃圾收集器方案永远不能正确地挪用finalize()。因此,finalize()要领(出格是那些用于封锁文件的)事实上常常都不会获得挪用。此刻有些文章声称所有收尾模块城市在措施退出的时候获得挪用——纵然到措施中止的时候,垃圾收集器仍未针对那些工具采纳动作。这并不是真实的环境,所以我们基础不能指望finalize()能为所有工具而挪用。出格地,finalize()在Java 1.0里险些毫无用处。
前面的措施向我们展现出:在Java 1.1中,收尾模块必定会运行这一许诺已成为现实——但前提是我们明晰地强制它采纳这一操纵。若利用一个不是“before”或“after”的自变量(如“none”),那么两个收尾事情都不会举办,并且我们会获得象下面这样的输出:
Created 47
Created 47 Beginning to finalize after 8694 Chairs have been created Finalizing Chair #47, Setting flag to stop Chair creation After all Chairs have been created: total created = 9834, total finalized = 108 bye!
#p#分页标题#e#
因此,到措施竣事的时候,并非所有收尾模块城市获得挪用(注释④)。为强制举办收尾事情,可先挪用System.gc(),再挪用System.runFinalization()。这样可排除到今朝为止没有利用的所有工具。这样做一个稍显奇怪的处所是在挪用runFinalization()之前挪用gc(),这看起来好像与Sun公司的文档说明有些抵触,它宣称首先运行收尾模块,再释放存储空间。然而,若在这里首先挪用runFinalization(),再挪用gc(),收尾模块基础不会执行。
④:到你读到本书时,有些Java虚拟机(JVM)大概已开始表示出差异的行为。
针对所有工具,Java 1.1有时之所以会默认为跳过收尾事情,是由于它认为这样做的开销太大。不管用哪种要领强制举办垃圾收集,都大概留意到比没有特别收尾事情时较长的时间延迟。