Finalizers are unpredictable ,often dangerous ,and generally unnecessary.

在Java中,GC会自动回收不可达对象相关的空间,而不需要程序员做相关的工作。
对于非内存资源,我们通常使用try-finally语句块进行释放。

finalizer不保证立即执行。
从一个对象编程不可达状态到调用finalizer,这段时间是任意的。
即,对时间敏感的操作不能在finalizer中进行。

never do anything time-critical in finalizer.

比如对一些资源对象进行close,file descriptor是有限资源,不及时关闭的后果很严重。
(PS:话说AutoCloseable的author也是Josh Bloch。)

哪些finalizer会及时得到执行,这个问题主要依赖于GC算法。
但GC算法在不同的JVM中的表现或多或少会不一样(我做过的项目都是用Hotspot...)。
一个程序可能在开发时运行得不错,而换了个环境可能会无法正常运行。
(比如finalizer的优先级太低,一直没得到回收,回收速度跟不上创建速度。)

不仅不保证finalizer执行的及时性,而且也不保证finalizer执行的可能性。
finalizer有可能根本得不到执行。
比如,很多不可达对象的finalizer尚未得到执行,此时程序被终止。

Never depend on a finalizer to update critical persistent state.

也不要期待以下方法为finalizer的执行提供保障:

System.gc();

System.runFinalization();

Runtime.runFinalizersOnExit(true);

System.runFinalizersOnExit(true);

值得一提的是,如果未捕获的异常在finalizer中抛出,这个异常会被无视掉,而且连stack trace都不会打印出来。
另外,finalizer有非常严重的性能损耗,这种东西最好尽量避免。

如果某个对象封装的资源(比如文件或者线程)需要终止,此时也不要指望finalizer。
而是提供一个显示的close方法(explicit termination method),并且加入一个记录资源可用状态的私有域。

那finalizer到底有什么意义?

  • 当忘记调用close方法时,finalizer可以当作最后的防线(原话是safety net,即安全网)。
    比如FileInputStream的close方法和finalizer,当私有的FileDescriptor不为空并且也不属于java.lang.System#in时调用显示的close方法:

    /**
    * Ensures that the <code>close</code> method of this file input stream is
    * called when there are no more references to it.
    *
    * @exception IOException if an I/O error occurs.
    * @see java.io.FileInputStream#close()
    */
    protected void finalize() throws IOException {
    if ((fd != null) && (fd != FileDescriptor.in)) { /*
    * Finalizer should not release the FileDescriptor if another
    * stream is still using it. If the user directly invokes
    * close() then the FileDescriptor is also released.
    */
    runningFinalize.set(Boolean.TRUE);
    try {
    close();
    } finally {
    runningFinalize.set(Boolean.FALSE);
    }
    }
    }
  • 处理本地对等体(native peer)。
    之前没有太关注"本地对等体"这个词汇。
    本地对等体是一个native对象,普通对象通过本地方法委托给一个本地对象。
    GC无法回收本地对等体,当本地对等体的java对等体被回收的时候,本地对等体不会被处理。
    如果本地对等体不用有关键资源(critical resource),finalizer可以胜任这项工作。

PS:对于本地对等体的解释,可以参考下面一段话:

一个AWT组件通常是一个包含了对等体接口类型引用的组件类。这个引用指向本地对等体实现。
java.awt.Label为例,它的对等体接口是LabelPeer。LabelPeer是平台无关的。
在不同平台上,AWT提供不同的对等体类来实现LabelPeer。在Windows上,对等体类是WlabelPeer,它调用JNI来实现label的功能。
这些JNI方法用C或C++编写。它们关联一个本地的label,真正的行为都在这里发生。
作为整体,AWT组件由AWT组件类和AWT对等体提供了一个全局公用的API给应用程序使用。
一个组件类和它的对等体接口是平台无关的。底层的对等体类和JNI代码是平台相关的。

(An AWT component is usually a component class which holds a reference with a peer interface type. This reference points to a native peer implementation.
Take java.awt.Label for example, its peer interface is LabelPeer.
LabelPeer is platform independent. On every platform, AWT provides different peer class which implements LabelPeer. On Windows, the peer class is WlabelPeer, which implement label functionalities by JNI calls.
These JNI methods are coded in C or C++. They do the actual work, interacting with a native label.
Let's look at the figure.
You can see that AWT components provide a universal public API to the application by AWT component class and AWT peers. A component class and its peer interface are identical across platform. Those underlying peer classes and JNI codes are different. )

另外,提到了finalizer chaining。
子类构造器会自动调用父类构造器,于是可能会想象子类finalizer自动调用父类finalizer。
如果子类复写了父类的finalizer,子类必须手动调用父类的finalier。

Java - 避免使用finalizer的更多相关文章

  1. Effective Java 第三版——8. 避免使用Finalizer和Cleaner机制

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  2. Java的Finalizer引发的内存溢出

    本文介绍的是Java里一个内建的概念,Finalizer.你可能对它对数家珍,但也可能从未听闻过,这得看你有没有花时间完整地看过一遍java.lang.Object类了.在java.lang.Obje ...

  3. 【java】jstack

    介绍 jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64",Windows的jsta ...

  4. Could not load java.net.BindException错误解决

    出现了错误异常:信息: Illegal access: this web application instance has been stopped already.  Could not load ...

  5. java jvm常用命令工具

    [尊重原创文章出自:http://www.chepoo.com/java-jvm-command-tools.html] 一.概述 程序运行中经常会遇到各种问题,定位问题时通常需要综合各种信息,如系统 ...

  6. java 堆栈分析

    再次,研究了一个下午的jhat好jmap.从一开始惊呆.懵懂于那样大量而无聊乏味的数据,到现在有那么一点点收货.赶紧记录下来.没办法,悟性太低... C:\Users\Administrator> ...

  7. java 锁!

    问题:如何实现死锁. 关键: 1 两个线程ta.tb 2 两个对象a.b 3 ta拥有a的锁,同时在这个锁定的过程中,需要b的锁:tb拥有b的锁,同时在这个锁定的过程中,需要a的锁: 关键的实现难点是 ...

  8. Java多线程之Runable与Thread

    Java多线程是Java开发中的基础内容,但是涉及到高并发就有很深的研究可做了. 最近看了下<Java并发实战>,发先有些地方,虽然可以理解,但是自己在应用中很难下手. 所以还是先回顾一下 ...

  9. Java调试

    线上load高的问题排查步骤是: 先用top找到耗资源的进程 ps+grep找到对应的java进程/线程 jstack分析哪些线程阻塞了,阻塞在哪里 jstat看看FullGC频率 jmap看看有没有 ...

随机推荐

  1. MongoDB集群搭建-分片

    MongoDB集群搭建-分片 一.场景: 1,机器的磁盘不够用了.使用分片解决磁盘空间的问题. 2,单个mongod已经不能满足写数据的性能要求.通过分片让写压力分散到各个分片上面,使用分片服务器自身 ...

  2. 一个简单的HTML病毒分析

    一直就想写这篇东西了,仅仅是上班时说要上班,不写.回家后又忙着玩游戏,丢一边去了.如今仅仅好不务正业的开写了.希望头儿不会知道我的blog.哈哈 在非常久之前就对HTML的病毒非常感兴趣了,非常好奇怎 ...

  3. 【转】ListBox Dock Fill 总是有空隙的问题

    源地址:https://www.cnblogs.com/norsd/p/6359291.html ListBox Dock设置了Fill, Right等 设计界面如己所愿,但是实际运行时,底部总是有不 ...

  4. 【SSO单点系列】开篇

    年底将至,忙碌了好几个月的项目也接近尾声了.在这个项目中,由于要和其他外系统做单点登录(SSO),整合其他系统的功能.在网上查询了相关资料后,最终选取了Yale大学发起的一个开源项目 CAS, 作为项 ...

  5. java执行命令行命令

    package javai; import java.io.BufferedReader; import java.io.InputStreamReader; public class IOTest ...

  6. 虚拟机安装CentOS,网络配置

    CentOS6和CentOS7,基础配置差不多. 安装CentOs6,过程:略: 安装完成后,系统默认启用动态ip,每次重启主机IP可能就会变化,搭配Xftp和Xshell工具会很难受,因此设置静态I ...

  7. [转](译)KVO的内部实现

    转载自:http://www.cocoachina.com/applenews/devnews/2014/0107/7667.html   09年的一篇文章,比较深入地阐述了KVO的内部实现.   K ...

  8. 对cookie,session,token,jwt的理解

    对这几个东西有点凌乱了,今天有时间整理下 cookie Cookie的诞生 由于HTTP协议是无状态的,而服务器端的业务必须是要有状态的.Cookie诞生的最初目的是为了存储web中的状态信息,以方便 ...

  9. Touchmove获取当前触摸的Dom节点

    原文链接:https://www.jianshu.com/p/a2a41fe20790 Touchmove获取当前触摸的Dom节点 近期为自己的app增加一个通讯录首字母导航的功能,如下图蓝色框部分, ...

  10. 2018年10月19 手记 - 身为开发者的我de窘境

    从10月1国庆过完节回来,那已经是7号了,之后便开始紧锣密鼓的筹划着接下来11月份的公司组织的对外活动,这边新来的产品对产品或者说对任务很是负责,并且策划了很多的方案,并且乐意站在我们开发的角度上去考 ...