5

所以,我最近在 Java 中发现了 finalize 方法(不知道为什么我之前错过了它,但它确实存在)。这似乎可以解决我正在处理的许多问题,但我想先获得更多信息。

在网上,我找到了这张图,说明了垃圾收集和完成的过程:

这描述了涉及 finalize 和 JGC 的操作顺序:

几个问题:

  1. 这发生在一个单独的线程中,对吗?
  2. 如果我在 finalize 期间实例化一个新对象会发生什么?这是允许的吗?
  3. 如果我从 finalize 调用静态方法会发生什么?
  4. 如果我从 finalize 中建立对对象的新引用会发生什么?

我想我应该解释一下我为什么感兴趣。我经常使用 LWJGL,似乎如果我可以使用 finalize 使 Java 对象自动清理 OpenGL 资源,那么我可以在 API 方面做一些非常好的事情。

4

3 回答 3

5

当 Java 垃圾收集器检测到不存在对该特定对象的引用时,它会调用 finalize()。finalize() 被所有 Java 对象通过 Object 类继承。

据我所知,从 finalize() 方法调用静态方法不会有任何困难,并且可以从 finalize() 建立对它的新引用 - 但是我会说这是糟糕的编程习惯。

您不应该依赖 finalize() 进行清理,最好边走边清理。我更喜欢使用 try、catch、finally 进行清理,而不是使用 finalize()。特别是,通过使用 finalize(),您将导致 JVM 保留您的可终结对象引用的所有其他对象,以防万一它调用它们。这意味着您持有可能不需要使用的内存。更重要的是,这也意味着您可以使 JVM 永远不会最终处理对象,因为它们必须保留它们以防另一个对象 finalize 方法需要它,例如竞争条件。

另外,请考虑完全有可能不调用 GC。因此,您实际上不能保证finalize() 将被调用。

我的建议是在完成资源后清理资源,不要依赖 finalize() 来完成。

于 2012-03-04T17:02:27.570 回答
3

我认为对于将使用哪个线程没有任何保证。可以实例化新对象并且可以调用静态方法。建立对您的对象的新引用将防止它被垃圾收集,但不会finalize再次调用该方法——您不想这样做。

清理资源正是该finalize方法的用途,所以你应该在那里做得很好。但是有几个警告:

不保证调用该方法。 如果您占用了在程序停止时不会自动释放的资源,请不要依赖finalize.

不保证何时调用该方法。记忆力紧,这会更快。有很多可用内存,如果有的话,它会更晚。这可能适合您:拥有大量内存,您可能不会那么担心释放资源。(虽然挂在他们身上可能会干扰同时运行的其他软件,在这种情况下你担心。)

我通常的解决方案是使用某种处理方法来进行清理。如果可以的话,我会在某个时候明确地调用它,并且尽可能快地调用它。然后我添加了一个只调用dispose方法的 finalize方法。(请注意,当多次调用时, dispose方法必须表现良好!确实,通过这种编程,我可能会在外部多次调用disposefinalize,不确定之前的调用是否成功,但希望尽快有效地调用它尽可能。)现在,理想情况下,一旦我不再需要我的资源就会被释放。但是,如果我失去了对具有资源的对象的跟踪,则finalize当内存不足并且我需要帮助时,该方法会帮助我。

于 2012-03-04T17:51:58.900 回答
1

首先,请记住,不能保证您的所有对象都会运行最终确定。您可以使用它来释放在与对象关联的本机代码中分配的内存,但对于纯 Java 代码,大多数用例仅用于执行清理资源的“备份”机制。这意味着在大多数情况下,您应该手动释放资源,如果您忘记以标准方式进行清理,终结器只能充当一种清理助手。但是,您不能将它们用作唯一或主要的清理机制。更一般地说,您不应该编写任何正确性取决于正在运行的终结器的代码。

广告 1. 据我所知,无法保证线程调用的内容finalize(),但实际上这可能是 GC 线程之一。

广告 2. 允许实例化新对象。但是,在终结器中处理对象引用存在许多缺陷。特别是,如果您在某个活动对象中存储对正在完成的对象的硬引用,则可以防止您的即将被垃圾收集的对象被清理。如果失去控制,这种对象复活可能会导致您的资源耗尽。此外,请注意异常finalize()- 它们可能会停止最终确定,但您的程序没有自动方式来了解它们。您需要将代码包装在 try-catch 块中并自己传播信息。此外,终结器的较长执行时间可能会导致对象队列堆积并消耗大量内存。其他一些值得注意的问题和限制在这篇 JavaWorld 文章

广告 3. 从终结器调用静态方法应该没有任何问题。

广告 4. 如第 2 点所述,可以通过在终结期间将对它的引用放置在另一个活动对象中来防止对象被垃圾收集(以复活它)。但是,这是一种棘手的行为,可能不是好的做法。

总而言之,你不能依赖终结器来清理你的资源。您需要手动处理该问题,并且在您的情况下,终结器最多只能用作备份机制,以在某种程度上马虎编码后进行掩盖。不幸的是,这意味着您通过使用终结器清理 OpenGL 资源来使 API 更好的想法可能行不通。

于 2012-03-04T17:23:18.080 回答