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. linux安装anaconda过程

    今天在centos7下安装了Anaconda,将安装过程记录如下 下载安装Anaconda 下载地址:https://repo.continuum.io/archive/index.html 打开页面 ...

  2. windows测试登陆

    测试工具我使用2种(Medusa和hydra): 第一种:Medusa支持端口登录但是不支持rdp协议,意思就是可以验证密码是否正确,新用户不会创建家目录: 使用方法: medusa -M smbnt ...

  3. django admin后台的简单使用

    创建自己的model.py文件 from django.db import models from django.contrib.auth.models import ( BaseUserManage ...

  4. 搜索下拉 select美化

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. 【随记】SQL备份一张表的数据

    SQL Server: SELECT  *  INTO  table_bak   FROM   table Oracle.MySQL.SQLite: CREATE TABLE table_bak AS ...

  6. windows 本地搭建 apache+mysql+php环境详细讲解

    1.mysql下载配置 可参考这篇文章:https://www.cnblogs.com/myIvan/p/9265645.html 2.php下载及配置 可参考这篇文章:https://www.cnb ...

  7. ghj1222被坑记录[不持续更新]

    考试注意事项:link1 link2 (密码:wangle) 调不出来bug,可以先透彻一会儿或者是上个厕所或者坐一会别的题(间隔至少20min),然后通读代码 -1. 考试先读题,读题之后搞出一个做 ...

  8. [CoffeeScript]在WebStorm里运行CoffeeScript

    CoffeeScript 是一门编译到 JavaScript 的小巧语言. 在 Java 般笨拙的外表下, JavaScript 其实有着一颗华丽的心脏. CoffeeScript 尝试用简洁的方式展 ...

  9. appium +android例子

    配置文件: # coding:utf-8 __author__ = 'Helen' """ description:配置全局参数 """ i ...

  10. [Alpha]Scrum Meeting#3

    github 本次会议项目由PM召开,时间为4月3日晚上10点30分 时长15分钟 任务表格 人员 昨日工作 下一步工作 木鬼 撰写团队贡献分配计划(issue#39) 调整&分配工作 SiM ...