Java 8:关于 finalize 方法的断言是否正确?

3
我一直在阅读Kathy Sierra + Bert Bates编写的《OCA Java SE 8程序员I考试指南》。 关于finalize方法,有些地方我还不太明白。书中第218页上写道:
“调用finalize()方法实际上可能会导致对象被保存而不被删除。”
后来在第222页上又提到:
“您可以在finalize()方法内使一个对象不再适合垃圾回收。”
我的母语不是英语,但我理解两种情况的意思是,finalize()方法可以防止对象被垃圾回收吗?这是真的吗?还是我误解了它的含义?

1
如果你在finalize中保存了对象的引用,我认为这会防止它被垃圾回收。 - Carcigenicate
3个回答

5

这其实更多是一个理论问题:当对象不再被其他对象引用时,它们就有资格进行垃圾回收。

因此:你可以在finalize()中尝试创建这样的引用。这理论上会阻止对象被移除。

实际上,这还有一个“模式名称”:对象复活。现在,这是一种模式,还是更多地属于反面模式,取决于争论。

(个人观点:我从不这样做,也从未遇到过需要使用这种模式的情况)


除了这是一种在垃圾收集器触发并尝试收集相关对象时调用某些逻辑的方法外,这确实与简单地存储对该对象的“全局”引用没有什么区别,对吗? - Fureeish
1
请注意,finalize() 方法只会被调用一次(如果它被调用的话)。在再次引用对象并稍后取消引用后,finalize() 方法不会再次被调用,对象可能会被直接丢弃。 - M A
1
@Fureeish嗯,当您在某个“全局存储”中添加引用时,您会在某个先前的时间点执行此操作。因此,从理论上讲,该对象永远不会变得合适,因此其finalize()永远不会被调用。 - GhostCat
@manouti 对象的 finalize() 方法不会再次被调用,但它不必使 this 实例复活,而是可以使另一个只能在 finalization 下由此对象访问的对象复活。它可以存储到另一个具有 finalize() 的对象中,以任意次数复活它。但实际上,finalize() 的调用已经是一种复活。将一个无限循环放入其中,对象就会永远存在,而无需存储全局引用。只要您拥有 finalizer 线程并且所有具有非平凡 finalize() 的对象都保持活动状态,就可以无限次执行此操作。 - Holger

2

Finalize是JVM调用的方法,而不是用户调用的方法。在对象被垃圾回收之前,该方法会被执行。您可以重写finalize方法,在对象被处理或销毁之前进行清理操作,如本书所述,您还可以防止对象被垃圾回收。

您可以参考下面的代码,以保存对象不被垃圾回收。

class Example { 

    static Example y; 

    void func() { 
        Example x = new Example(); 
    } 

    pubic void finalize() { 

        y = this; // Putting the reference id 
        // of the current object 
        // into the static variable y 

        System.out.println("The object won't be collected by the garbage collector"); 

    } 

    public static void main(String a[]) {
        func(); // function called 
    } 
}

是的,就像这个主题所展示的那样。https://dzone.com/articles/java-immortal-objects-and-object-resurrection - chiperortiz
@chiperortiz 是的,当调用 finalize 方法时,您始终可以使用 this 关键字引用正在被销毁的对象,并从该特定对象执行所需操作,其中一种用法是复活。 - Shubham Saraswat

0

嗯,那是真的。

finalize()方法在GC决定删除对象时被调用,但这并不意味着对象会在finalize完成后立即被删除。

IT DOESN'T WORK THIS WAY:
_________________________
IF shouldBeRemoved(object)
   object.finalize();
   remove(object);

finalize 执行后,GC会再次检查该对象是否应该被移除。为了符合移除的条件,一个对象不应被任何可从根对象到达的对象引用。

IT WORKS THIS WAY
_________________
LABEL
IF shouldBeRemoved(object)
    object.finalize();
    IF shouldBeRemoved(object)
        remove(object);
    ELSE
        GOTO LABEL

让我们想象以下情况:

class Foo {
    Application a;

    Foo(){};

    @Override
    public void finalize() {
        this.a = Application.getInstance();
    }
}

Application 是代表 Application 根对象的类。在这种情况下,由于 a 仍然可达,因此在之前有资格被删除的 Foo 类对象刚刚复活。

重要提示

不能保证 finalize 方法会被调用,因为它需要另一个对象来处理 finalize 方法的调用,所以如果堆上没有足够的空闲空间,对象可能会在不调用 finalize 的情况下被销毁。


1
Application实例分配给实例变量如何使得该实例保持可达性?它并没有将对this的引用分配给其他位置,因此我不明白该实例如何通过自己的实例变量可达。 - Lew Bloch
1
关于“对象可能在不调用finalize的情况下被销毁”的说法是错误的。此外,我不理解“它需要另一个对象来处理finalize方法的调用”。什么其他对象?这也不正确。 - Lew Bloch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接