12

我对定稿的理解是这样的:

为了清理或回收对象占用的内存,垃圾收集器开始行动。(自动被调用?)

然后垃圾收集器取消引用该对象。有时,垃圾收集器无法访问对象。然后调用 finalize 进行最终清理处理,之后可以调用垃圾收集器。

这是对最终确定的准确描述吗?

4

5 回答 5

16

垃圾收集器在后台自动工作(虽然它可以被显式调用,但应该很少需要这样做)。它基本上只清理不被其他对象引用的对象(当然,全貌更复杂,但这是基本思想)。因此它不会更改任何活动对象中的任何引用。如果无法从任何活动对象访问对象,这意味着可以安全地对它进行垃圾回收。

终结意味着清理对象获取的资源(不是内存,而是其他资源,例如文件句柄、端口、数据库连接等)但是,它并没有真正解决:-(

  • 什么时候finalize()被调用是不可预测的
  • 事实上,不能保证finalize()永远会被调用!

所以即使保证会被调用,它也不是释放资源的好地方:当调用它释放你打开的所有DB连接时,系统可能已经完全耗尽了空闲连接,并且您的应用程序不再工作。

于 2010-03-15T21:23:39.460 回答
8

这篇文章

实现 finalize() 方法的类的任何实例通常称为可终结对象。当它们不再被引用时,Java 垃圾收集器不会立即回收它们。取而代之的是,Java 垃圾收集器将对象附加到一个特殊的队列中以进行终结处理。通常它是由一些 Java 虚拟机上称为“引用处理程序”的特殊线程执行的。在这个终结过程中,“终结器”线程将执行对象的每个 finalize() 方法。只有在 finalize() 方法成功完成后,才会将一个对象移交给 Java 垃圾收集,以便“未来”的垃圾收集回收其空间。

您几乎可以在类的 finalize() 方法中自由地做任何事情。当您这样做时,请不要期望当不再引用或不再需要对象时,Java 垃圾收集器会回收每个对象占用的内存空间。为什么?不保证 finalize() 方法会及时完成执行。最坏的情况是,即使没有更多对该对象的引用,它也可能不会被调用。这意味着不能保证任何具有 finalize() 方法的对象都会被垃圾回收。

此外, Sun 的这篇文章有一些很好的图表来解释这个过程。

于 2010-03-15T21:26:22.567 回答
6

没有。该finalize()方法仅在垃圾收集器尝试回收您的对象时运行。

您的对象使用的任何内存(通常,我想不出异常)会自动连接到您的对象并与它一起清理。因此,终结并不意味着释放内存,而是你的对象可能与之关联的任何其他资源。例如,这可以用来关闭打开的文件或数据库连接,或者可能运行一些与操作系统接口的低级代码以释放一些系统级资源。

于 2010-03-15T21:24:43.470 回答
5

实际上,这是 finalize() 方法的行为:

一旦垃圾收集器运行(VM 决定它需要释放内存,您不能强制它运行)并决定从该对象收集内存(这意味着不再有指向它的引用,至少来自可访问对象) ,就在它删除它占用的内存之前,它在对象上运行方法 finalize()。您可以确定,如果垃圾收集,对象将在它消失之前运行 finalize(),但您不能确定它是否会被 GC 处理,因此您根本不应该依赖该方法进行任何清理. 您应该在 finally {} 块中运行清理语句,而不是使用 finalize(),因为它不能保证运行。

此外,有些人进行了性能测试,并表明 finalize 方法在一定程度上减慢了对象的创建/销毁速度。我不记得来源,所以将此信息视为不太可靠。:)

于 2010-03-15T21:28:04.113 回答
2

终结用于清理垃圾收集器无法释放的资源。例如,考虑一个native直接从操作系统分配(通过一些 API)资源的程序。这通常会产生某种“句柄”(UNIX 文件描述符或 Windows HANDLE,或类似的东西):

class Wrapper {
    private long handle;

    private Handle(long h) {
        handle = h;
    }

    private static native long getHandleFromOS();

    static Wrapper allocate() {
        return new Handle(getHandleFromOS());
    }
}

那么,如果您的代码分配了 class 的实例,会发生什么Wrapper?好吧,该类分配某种特定于操作系统的资源,并在成员变量中保留对它的引用(句柄)。但是,当最后一个对包装器实例的 Java 引用丢失时会发生什么?现在,垃圾收集器将(在某个时候)回收现已失效的包装器实例的空间。但是包装器分配的操作系统资源会发生什么?在上面的场景中它会被泄露,这是一件坏事,如果它是一个昂贵的资源,比如文件描述符。

为了让您的代码在这种情况下进行清理,有finalize方法。

class Wrapper {
    private long handle;

    private Handle(long h) {
        handle = h;
    }

    protected void finalize() {
        returnHandleToOS(handle);
    }

    private static native long getHandleFromOS();
    private static native void returnHandleToOS(long handle);

    static Wrapper allocate() {
        return new Handle(getHandleFromOS());
    }
}

现在,当 GC 回收包装实例的空间时,终结器会确保资源正确返回给操作系统。

这听起来不错,但正如其他人已经指出的那样,缺点是终结器本质上是不可靠的:你不知道终结器何时运行。更糟糕的是:根本无法保证它会运行。所以最好提供一种dispose机制并仅使用终结作为安全网,以防您的类的客户忘记正确处理他们的引用:

class Wrapper {
    private long handle;

    private Handle(long h) {
        handle = h;
    }

    protected void finalize() {
        if( handle != 0 ) returnHandleToOS(handle);
    }

    public void dispose() {
        returnHandleToOS(handle);
        handle = 0;
    }

    private static native long getHandleFromOS();
    private static native void returnHandleToOS(long handle);

    static Wrapper allocate() {
        return new Handle(getHandleFromOS());
    }
}
于 2010-03-15T21:37:18.287 回答