如何确保始终调用finalize()(在Java练习中思考)

我正在慢慢地完成Bruce Eckel的Thinking in Java第4版 ,以下问题让我难过:

使用finalize()方法创建一个打印消息的类。 在main()中,创建一个类的对象。 修改上一个练习,以便始终调用finalize()。

这是我编码的:

public class Horse { boolean inStable; Horse(boolean in){ inStable = in; } public void finalize(){ if (!inStable) System.out.print("Error: A horse is out of its stable!"); } } public class MainWindow { public static void main(String[] args) { Horse h = new Horse(false); h = new Horse(true); System.gc(); } } 

它创建一个新的Horse对象,其布尔值inStable设置为false 。 现在,在finalize()方法中,它检查inStable是否为false 。 如果是,则打印一条消息。

不幸的是,没有打印消息。 由于条件的计算结果为true ,我的猜测是首先没有调用finalize() 。 我已经多次运行该程序,并且已经看到错误消息只打印了几次。 我的印象是,当调用System.gc()时,垃圾收集器将收集任何未引用的对象。

谷歌搜索一个正确的答案给了我这个链接 ,它提供了更详细,更复杂的代码。 它使用我以前从未见过的方法,例如System.runFinalization()Runtime.getRuntime()System.runFinalizersOnExit()

是否有人能够让我更好地理解finalize()如何工作以及如何强制它运行,或者让我了解解决方案代码中正在完成的工作?

当垃圾收集器找到一个符合收集条件但有finalizer器的对象时,它不会立即解除分配。 垃圾收集器尝试尽快完成,因此它只是将对象添加到具有挂起终结器的对象列表中。 稍后在单独的线程上调用终结器。

您可以通过在垃圾回收后调用方法System.runFinalization来告诉系统立即尝试运行挂起的终结器。

但是如果你想强制终结器运行,你必须自己调用它。 垃圾收集器不保证将收集任何对象或将调用终结器。 它只是“尽力而为”。 但是,您很少需要强制终结器在实际代码中运行。

在玩具场景之外,通常不可能确保始终在没有“有意义”引用的对象上调用finalize ,因为垃圾收集器无法知道哪些引用是“有意义的”。 例如,类似ArrayList的对象可能具有“clear”方法,该方法将其计数设置为零,并使后备arrays中的所有元素都有资格被未来的Add调用覆盖,但实际上并未清除该后备中的元素arrays。 如果对象有一个大小为50的数组,并且其Count为23,则可能没有执行路径,代码可以通过该路径检查存储在数组的最后27个插槽中的引用,但是没有办法处理垃圾-collector知道这一点。 因此,垃圾收集器永远不会对这些插槽中的对象调用finalize ,除非或直到容器覆盖了那些数组插槽,容器放弃了数组(可能有利于更小的数组),或者对容器本身的所有有根引用都被销毁或以其他方式不复存在。

有各种方法可以鼓励系统对任何没有强根源引用的对象调用finalize (这似乎是问题的关键,以及其他答案已经涵盖的内容),但我认为重要的是要注意存在强根引用的对象集与代码可能感兴趣的对象集之间的区别。这两个集很大程度上重叠,但每个集可以包含不在另一个中的对象。 当GC确定对象不再存在但是存在终结器时,对象的终结器运行; 可能会或可能不会与他们停止对任何人感兴趣的时间码重合。 虽然如果可以使终结器在所有不再感兴趣的对象上运行会很有帮助,但这通常是不可能的。

对garabage collecter( System.gc() )方法的调用表明 ,Java虚拟机花费了很多精力来回收未使用的对象,以使它们当前占用的内存可用于快速重用(即它只是对jvm的建议,并且不绑定它来执行动作然后,它可能会或可能不会这样做)。 当控制从方法调用返回时,Java虚拟机已尽最大努力从所有丢弃的对象中回收空间。 当垃圾收集确定没有对该对象的更多引用时,对象上的垃圾收集器调用finalize()

运行新的构造函数()和System.gc()两次以上。

 public class Horse { boolean inStable; Horse(boolean in){ inStable = in; } public void finalize(){ if (!inStable) System.out.print("Error: A horse is out of its stable!"); } } public class MainWindow { public static void main(String[] args) { for (int i=0;i<100;i++){ Horse h = new Horse(false); h = new Horse(true); System.gc(); } } } 

这对我有用(部分,但确实说明了这个想法):

 class OLoad { public void finalize() { System.out.println("I'm melting!"); } } public class TempClass { public static void main(String[] args) { new OLoad(); System.gc(); } } 

OLoad(); 诀窍,因为它创建一个没有附加引用的对象。 这有助于System.gc()运行finalize()方法,因为它检测到没有引用的对象。 说像OLoad o1 = new OLoad(); 将无法工作,因为它将创建一个直到main()结束的引用。 不幸的是,这大部分时间都有效 。 正如其他人所指出的那样,除了自己调用之外,没有办法确保始终调用finalize()