代码之家  ›  专栏  ›  技术社区  ›  Spencer Kormos

为什么要实现finalize()?

  •  344
  • Spencer Kormos  · 技术社区  · 16 年前

    我已经阅读了很多关于Java的新手问题 finalize() 并发现令人困惑的是,没有人真正明确表示finalize()是一种不可靠的清理资源的方法。我看到有人评论说他们用它来清理连接,这真的很可怕,因为要保证连接关闭,唯一的办法就是最终实现try(catch)。

    我没有接受过CS方面的教育,但我已经用Java专业编程近十年了,我从未见过有人实现 定稿 在一个生产系统中。这并不意味着它没有用处,也不意味着与我共事的人做得对。

    所以我的问题是,有什么用例可以实现 定稿 通过语言中的另一个进程或语法无法更可靠地处理这些问题?

    21 回复  |  直到 3 年前
        1
  •  238
  •   Hearen    5 年前

    您可以将其用作持有外部资源(套接字、文件等)的对象的后盾。实施 close() 方法和需要调用的文档。

    使生效 finalize() 关闭() 如果检测到未完成,则进行处理。也许有什么东西丢给我了 stderr 指出你正在清理一个有问题的来电者。

    它在特殊情况下提供了额外的安全性。并不是每个打电话的人都会做正确的事情 try {} finally {}

    我同意很少需要它。正如评论者指出的,它伴随着GC开销。只有在长时间运行的应用程序中需要“安全带和吊带”时才使用。

    我认为从Java9开始, Object.finalize() 不推荐使用!他们指给我们看 java.lang.ref.Cleaner java.lang.ref.PhantomReference

        2
  •  180
  •   Hearen    5 年前

    finalize() 是对JVM的一个提示,提示在未指定的时间执行代码可能很好。当您希望代码神秘地无法运行时,这是很好的。

    在终结器中执行任何重要的操作(基本上除了日志记录之外的任何操作)在以下三种情况下也很好:

    • 您希望向所有具有终结器的类的所有方法添加大量检查代码,以确保它们在终结后的行为正确。
    • 您希望意外地恢复已完成的对象,并花费大量时间试图找出它们为什么不工作,和/或它们最终发布时为什么没有完成。

    幻象引用 要求 收集前应采取的具体行动?这让我们回到了malloc/free的时代。)

    其他时候,你需要你认为你正在管理的资源来变得更加健壮。例如,为什么需要关闭该连接?它最终必须基于系统提供的某种类型的I/O(套接字、文件等),因此,当最低级别的资源是gced时,为什么不能依靠系统来关闭它呢?如果另一端的服务器绝对要求您干净地关闭连接,而不是仅仅断开插座,那么当有人在运行代码的机器的电源线上绊倒,或者中间的网络断开时,会发生什么情况?

    免责声明:我过去曾参与过JVM实现。我讨厌终结器。

        3
  •  57
  •   Hearen    5 年前

    一条简单的规则:永远不要使用终结器。

    仅仅一个对象有一个终结器(不管它执行什么代码)的事实就足以导致垃圾收集的大量开销。

    article 布莱恩·戈茨:

    具有终结器的对象(那些 有一个非平凡的finalize()方法) 与之相比,具有显著的开销 节约使用。可定稿 对象分配都比较慢 收集速度较慢。分配时 带有垃圾的可终结对象 热点(JVM实现) 分配路径比大多数其他路径慢 对象的收集速度也较慢。信息技术 至少进行两次垃圾收集 循环(在最佳情况下)在 可以回收可终结对象, 垃圾收集器必须做些什么 调用终结器的额外工作。 结果是花费了更多的时间 分配和收集对象和 收集器,因为 无法访问的可终结对象是 保留时间更长。再加上 终结器不是 时间框架,甚至是根本,你可以 要注意的是,有相对较少的 使用正确的工具。

        4
  •  49
  •   skaffman    16 年前

    我在生产代码中使用finalize的唯一一次是执行一个检查,检查给定对象的资源是否已清理,如果没有,则记录一条非常响亮的消息。它实际上并没有尝试自己去做,如果做得不好,它会大喊大叫。结果证明非常有用。

        5
  •  38
  •   Hearen    5 年前

    finalize() . 一次也没有。

        6
  •  29
  •   Bill K    5 年前

    公认的答案是好的,我只是想补充一点,现在有一种方法可以实现finalize的功能,而无需实际使用它。

    看看“参考”类。弱参考、幻影参考和;软参考。

    放心 被称为。

    关于最后确定:

    public void finalize() {
      ref1 = null;
      ref2 = null;
      othercrap = null;
    }
    

    如果您在finalize中发现这样的代码,那么可以保证编写它的人会感到困惑。

    如果它在其他地方,则可能是代码是坏模型的有效补丁(一个类停留很长时间,并且由于某些原因,它引用的东西必须在对象被GC调用之前手动释放)。一般来说,这是因为有人忘记删除一个侦听器或其他东西,并且不明白为什么他们的对象没有被GC删除,所以他们只是删除它所指的东西,耸耸肩然后走开。

    它永远不应该被用来“更快地”清理东西。

        7
  •  28
  •   Hearen    5 年前

    我不知道你能从中得到什么,但是。。。

    itsadok@laptop ~/jdk1.6.0_02/src/
    $ find . -name "*.java" | xargs grep "void finalize()" | wc -l
    41
    

    所以我想太阳找到了 一些

        8
  •  20
  •   Hearen    5 年前
    class MyObject {
        Test main;
    
        public MyObject(Test t) {    
            main = t; 
        }
    
        protected void finalize() {
            main.ref = this; // let instance become reachable again
            System.out.println("This is finalize"); //test finalize run only once
        }
    }
    
    class Test {
        MyObject ref;
    
        public static void main(String[] args) {
            Test test = new Test();
            test.ref = new MyObject(test);
            test.ref = null; //MyObject become unreachable,finalize will be invoked
            System.gc(); 
            if (test.ref != null) System.out.println("MyObject still alive!");  
        }
    }
    

    结果:

    This is finalize
    
    MyObject still alive!
    

    =====================================

    所以您可以在finalize方法中使不可访问的实例可访问。

        9
  •  11
  •   Hearen    5 年前

    finalize()

    我从1.0 alpha 3(1995)开始就一直在用Java编程,我还没有覆盖finalize的任何内容。。。

        10
  •  6
  •   Bill the Lizard    16 年前

        11
  •  5
  •   Michael Easter    16 年前

    要强调以上答案中的一点:终结器将在单独的GC线程上执行。我听说过一个主要的Sun演示,开发人员在一些终结器上添加了一个小睡眠,并故意将一个其他方面都很花哨的3D演示带到它的膝盖上。

    Eckel在Java中的思想 a good section 在这个问题上。

        12
  •  5
  •   Hearen    5 年前

    在工作中要小心 finalize() . 尤其是在调用close()以确保资源被清理的情况下。我们遇到了几种情况,其中JNI库链接到正在运行的java代码中,在任何使用finalize()调用JNI方法的情况下,我们都会得到非常严重的java堆损坏。损坏不是由底层JNI代码本身造成的,本机库中的所有内存跟踪都很好。事实上,我们是从finalize()调用JNI方法的。

    直到很久以后,我们才发现出现了问题,但最终罪魁祸首总是使用JNI调用的finalize()方法。

        13
  •  4
  •   Hearen    5 年前

    嗯,我曾经用它来清理没有返回到现有池中的对象。

    他们被传递了很多次,所以不可能知道他们什么时候可以安全地回到游泳池。问题是它在垃圾收集过程中引入了一个巨大的惩罚,这个惩罚远远大于合并对象所节省的成本。在我拆掉整个水池,使一切充满活力并完成之前,它已经生产了大约一个月。

        14
  •  3
  •   John Meagher    16 年前

    许多数据库驱动程序在其语句和连接实现中这样做是为了对忘记调用close的开发人员提供一点安全性。

        15
  •  2
  •   user265243    14 年前

    编辑:好吧,它真的不起作用。我实现了它,并认为如果它有时失败,对我来说没关系,但它甚至一次都没有调用finalize方法。

    我不是一名专业程序员,但在我的程序中,我有一个案例,我认为这是使用finalize()的一个很好的案例,这是一个缓存,它在销毁内容之前将内容写入磁盘。因为没有必要每次销毁时都执行它,它只会加速我的程序,我希望我没有做错。

    @Override
    public void finalize()
    {
        try {saveCache();} catch (Exception e)  {e.printStackTrace();}
    }
    
    public void saveCache() throws FileNotFoundException, IOException
    {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("temp/cache.tmp"));
        out.writeObject(cache);
    }
    
        16
  •  2
  •   Alexander Malfait    13 年前

    删除已添加到全局/静态位置(不需要)且在删除对象时需要删除的内容非常方便。例如:

        private void addGlobalClickListener() {
            weakAwtEventListener = new WeakAWTEventListener(this);
    
            Toolkit.getDefaultToolkit().addAWTEventListener(weakAwtEventListener, AWTEvent.MOUSE_EVENT_MASK);
        }
    
        @Override
        protected void finalize() throws Throwable {
            super.finalize();
    
            if(weakAwtEventListener != null) {
                Toolkit.getDefaultToolkit().removeAWTEventListener(weakAwtEventListener);
            }
        }
    
        17
  •  0
  •   JGFMK    11 年前

    iirc—您可以使用finalize方法作为实现昂贵资源池机制的一种手段—这样他们就不会得到GC。

        18
  •  0
  •   Hearen    5 年前

    作为旁注:

    资料来源: finalize() deprecated on java-9

        19
  •  0
  •   Hearen    5 年前

    一旦我们使用完资源(文件、套接字、流等),就需要关闭它们。他们通常有 close() 我们通常调用的方法 finally try-catch 声明。有时 finalize() 也可以由少数开发人员使用,但IMO认为这不是一种合适的方式,因为不能保证总是调用finalize。

    在Java7中,我们有 try-with-resources

    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
      // Processing and other logic here.
    } catch (Exception e) {
      // log exception
    } finally {
      // Just in case we need to do some stuff here.
    }
    

    在上面的示例中,try with resource将自动关闭资源 BufferedReader 关闭() Closeable 在我们自己的类中,并以类似的方式使用它。在我看来,这似乎更简洁易懂。

        20
  •  0
  •   Hearen    5 年前

    就我个人而言,我几乎从未使用过 finalize() 除了一种罕见的情况:我创建了一个自定义泛型类型集合,并编写了一个自定义泛型类型集合 方法执行以下操作:

    public void finalize() throws Throwable {
        super.finalize();
        if (destructiveFinalize) {
            T item;
            for (int i = 0, l = length(); i < l; i++) {
                item = get(i);
                if (item == null) {
                    continue;
                }
                if (item instanceof Window) {
                    ((Window) get(i)).dispose();
                }
                if (item instanceof CompleteObject) {
                    ((CompleteObject) get(i)).finalize();
                }
                set(i, null);
            }
        }
    }
    

    ( CompleteObject 是我制作的一个接口,它允许您指定很少实现的实现 Object 方法如 #finalize() , #hashCode() #clone() )

    那么,用一个姐姐 #setDestructivelyFinalizes(boolean) 方法时,使用my collection的程序可以(帮助)保证销毁对此集合的引用也会销毁对其内容的引用,并处理可能会无意中使JVM保持活动状态的任何窗口。我也考虑过停止任何线程,但这打开了一个全新的蠕虫罐。

        21
  •  0
  •   Hearen    5 年前

    接受的答案列出了可以在finalize期间关闭资源的列表。

    然而 this answer 显示了至少在使用JIT编译器的java8中,您会遇到意外的问题,有时甚至在您从对象维护的流中读取完之前就调用终结器。

    被推荐。