Java - 避免使用finalizer
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的更多相关文章
- Effective Java 第三版——8. 避免使用Finalizer和Cleaner机制
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- Java的Finalizer引发的内存溢出
本文介绍的是Java里一个内建的概念,Finalizer.你可能对它对数家珍,但也可能从未听闻过,这得看你有没有花时间完整地看过一遍java.lang.Object类了.在java.lang.Obje ...
- 【java】jstack
介绍 jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64",Windows的jsta ...
- Could not load java.net.BindException错误解决
出现了错误异常:信息: Illegal access: this web application instance has been stopped already. Could not load ...
- java jvm常用命令工具
[尊重原创文章出自:http://www.chepoo.com/java-jvm-command-tools.html] 一.概述 程序运行中经常会遇到各种问题,定位问题时通常需要综合各种信息,如系统 ...
- java 堆栈分析
再次,研究了一个下午的jhat好jmap.从一开始惊呆.懵懂于那样大量而无聊乏味的数据,到现在有那么一点点收货.赶紧记录下来.没办法,悟性太低... C:\Users\Administrator> ...
- java 锁!
问题:如何实现死锁. 关键: 1 两个线程ta.tb 2 两个对象a.b 3 ta拥有a的锁,同时在这个锁定的过程中,需要b的锁:tb拥有b的锁,同时在这个锁定的过程中,需要a的锁: 关键的实现难点是 ...
- Java多线程之Runable与Thread
Java多线程是Java开发中的基础内容,但是涉及到高并发就有很深的研究可做了. 最近看了下<Java并发实战>,发先有些地方,虽然可以理解,但是自己在应用中很难下手. 所以还是先回顾一下 ...
- Java调试
线上load高的问题排查步骤是: 先用top找到耗资源的进程 ps+grep找到对应的java进程/线程 jstack分析哪些线程阻塞了,阻塞在哪里 jstat看看FullGC频率 jmap看看有没有 ...
随机推荐
- dom4j学习总结(一)
dom4j学习总结(一) (一)创建Document的基本操作 /** * xml基本操作 */ public void BaseOperation(){ //创建一个document Doc ...
- tf-idf sklearn
第一步:语料转化为词袋向量 step 1. 声明一个向量化工具vectorizer: 本文使用的是CountVectorizer,默认情况下,CountVectorizer仅统计长度超过两个字符的词, ...
- 691. Stickers to Spell Word
We are given N different types of stickers. Each sticker has a lowercase English word on it. You wou ...
- “全栈2019”Java第八章:IntelliJ IDEA设置注释不显示在行首
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- [Winter Vacation] 守护,守望
最近总是堕落......想好了,不如在百无聊赖之时写一些心底的话,让它们最终不归于尘土吧. 有了想要守护一个人的信念与想法,然而有没有资格却还没有人能够说清楚,下断言.这可真是可悲了,总不能笃定着对方 ...
- linux安装报错之:ifconfig command not found解决
问题描述: 用虚拟机VMware安装linux系统(镜像文件是从官网下载的CentOS-7.0-1406-x86_64-DVD.iso), 在安装完成之后,输入ifconfig命令报错:ifconfi ...
- php 中将完整的年月日时分秒的时间转换成 年月日的形式
strtotime() 函数将任何英文文本的日期或时间描述解析为 Unix 时间戳(自 January 1 1970 00:00:00 GMT 起的秒数), 将完整的时间格式转换成时间撮的形式,再去进 ...
- Deeplearning学习
Deeplearning 概念 Deep Learning: 观点: 认为AI是最新的电力,大约在一百年前,我们社会的电气化改变了每个主要行业,从交通运输行业到制造业.医疗保健.通讯等方面,我认为 ...
- python3的enumerate函数
enumerate() 函数用于将一个可遍历的数据对象(如列表.元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中.
- 01Trie树 CF923C Perfect Security
CF923C Perfect Security 上下各n个数,求一种排列p,使上面的数i异或pi成为新的数i,求方案另字典序最小,输出该结果 01Trie树. 记录每个节点经过多少次. 每一次查询的时 ...