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看看有没有 ...
随机推荐
- Strings in the Pocket(马拉车+字符串判断)
题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=6012 BaoBao has just found two strings ...
- Django-02路由层
U RL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表:你就是以这种方式告诉Django,对于客户端发来的某个URL调用哪一段逻辑代 ...
- 量化分析获取数据的3种姿势(压箱底的神器Tushare)
自打入门量化分析起,就有相当部分的时间在与数据打交道,从数据的获取.清洗到使用,对分析而言既是繁琐的,也是必须的.有大牛曾经说,量化分析有8成的开发时间都在处理数据. 为了节省时间,将更多精力投入到策 ...
- noip提高组模拟赛(QBXT)T2
T2count题解 [ 问题描述]: 小 A 是一名热衷于优化各种算法的 OIER,有一天他给了你一个随机生成的 1~n 的排列, 并定 义区间[l,r]的价值为: \[ \huge C_{l,r}= ...
- P01-Python中列表的复制问题
(1)使用=直接赋值 a = [1, 2, 3, [6, 7]] b = a 初始情况: a: [1, 2, 3, [6, 7]] b: [1, 2, 3, [6, 7]] ------------- ...
- try语句块和异常处理
在C++中,异常处理包括: · throw表达式(throw expression) 异常检测部分使用throw表达式来表示它遇到了无法处理的问题.throw表达式抛出一个异常并把控制权转移到能处理该 ...
- 高阶篇:4.2.5)DFMEA建议措施及后续完备
本章目的:填写建议措施及DFMEA后续完备. 1.建议措施(k) 定义 总的来说,预防措施(降低发生率)比探测措施更好.举例来说,比起设计定稿后的产品验证/确认,使用已证实的设计标准或最佳实践更加可取 ...
- js中元素、触点等各种距离的总结
每次碰到元素滚动呀.鼠标拖动呀之类的通过对比位置来触发事件的需求时,都要花很多时间来百度怎么取到自己想要的那个值,什么scrollTop.offset等等,今天就把这些东西总结一下,以后再使用的话,就 ...
- Canvas 同心圆旋转示例解析
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Oracle知识转储
https://blog.csdn.net/u011479200/article/details/53086411 https://www.cnblogs.com/LiYi-Dao/p/9406189 ...