`
saybody
  • 浏览: 870724 次
  • 性别: Icon_minigender_2
  • 来自: 西安
文章分类
社区版块
存档分类
最新评论

Java性能优化[4]:关于finalize函数

阅读更多

  上次的帖子 聊了垃圾回收器的调优,当时啰嗦了比较长的篇幅,就没再继续提finalize的事儿(其实这玩意儿和GC是沾点儿边的)。今天咱就把finalize函数相关的性能话题拿来说一下。<!-- program-think-->

  ★finalize函数的调用机制
  俺经常啰嗦了解本质机制的重要性。所以今天也得先谈谈finalize函数的调用机制。在聊之前,先声明一下:Java虚拟机规范(见“这里 ”),并没有硬性规定垃圾回收该不该搞,以及该如何搞。所以俺这里提到的finalize函数的调用机制,或许适用于大多数JVM,但不保证能适用于所有 的JVM。
  ◇何时被调用?
  finalize啥时候才会被调用捏?一般来说,要等到JVM开始进行垃圾回收的时候,它才有可能 被调用。而JVM进行垃圾回收的时间点是非常 不确定的,依赖于各种运行时的环境因素。具体细节可以参见“本系列前一帖 ”。正是由于finalize函数调用时间点的不确定,导致了后面提到的某些缺点。
  ◇谁来调用?
  说完何时调用,咱接着来聊一下被谁调用?
   常见的JVM会通过GC的垃圾回收线程来进行finalize函数的调用。由于垃圾回收线程比较重要(人家好歹也是JVM的一个组成部分嘛),为了防止 finalize函数抛出的异常影响到垃圾回收线程的运作,垃圾回收线程会在调用每一个finalize函数时进行try catch,如果捕获到异常,就直接丢弃,然后接着处理下一个失效对象的finalize函数。

  ★finalize函数的误解和误用
  ◇把finalize当成“析构函数”
  学过C++的同学应该都知道“析构函数”(不懂C++的同学直接跳过此小节)。C++析构函数是在对象离开作用域的当口,立即 被调用的。很多从C++转Java的同学会想当然地把finalize函数牵强附会成C++的析构函数(两者确实有某些相似之处)。然而,现实往往不是这么美好滴。由于Java的finalize函数和C++的析构函数之间有许多非常显著 的差异,那些把finalize拿来当析构函数用的同学,注定是要碰壁滴(具体请看本文后面“finalize函数的缺点”)。
  ◇依靠finalize来释放资源
  很多同学寄希望于通过finalize()来完成类对象中某些资源的释放(比如关闭数据库连接之类)。有这种企图的同学,请注意看本文后面的“finalize函数的缺点”!

  ★使用finalize函数的注意事项
  下面介绍的注意事项,有些可能和性能优化关系不大,俺也一并列出来。
  ◇调用时间不确定——有资源浪费的风险
  前面已经介绍了调用机制。同学们应该认清“finalize的调用时机是很不确定的 ” 这样一个事实。所以,假如你把某些稀缺资源放到finalize()中释放,可能会导致该稀缺资源等上很久很久很久以后才被释放。这可是资源的浪费啊!另 外,某些类对象所携带的资源(比如某些JDBC的类)可能本身就很耗费内存,这些资源的延迟释放会造成很大的性能问题。
  ◇可能不被调用——有资源泄漏的风险
  很多同学以为finalize()总是会 被调用,其实不然。在某些情况下,finalize()压根儿不被调用。比如在JVM退出的当口,内存中那些对象的finalize函数可能就不会被调用了。
   俺估摸着会有同学在打“runFinalizersOnExit”的主意,来确保所有的finalize在JVM退出前被调用。很可惜也很遗憾,该方法 从JDK 1.2开始,就已经被废弃了。即使该方法不被废弃,也是有很大的线程安全隐患滴!企图打这个主意的同学,趁早死了这条心吧。
  从上述可以看出,一旦你依赖finalize()来帮你释放资源,那可是很不妙啊(有资源泄漏的危险 )!关于资源泄漏的严重性,俺在“这里 ”曾经提到过。很多时候,资源泄露导致的性能问题更加严重,万万不可小看。
  ◇对象可能在finalize函数调用时复活——有诈尸的风险
  诈尸的情况比较少见,不过俺还是稍微提一下。
   本来,只有当某个对象已经失效(没有引用),垃圾回收器才会调用该对象的finalize函数。但是,万一碰上某个变态的程序员,在 finalize()函数内部再把对象自身的引用(也就是this)重新保存在某处,也就相当于把自己复活了(因为这个对象重新有了引用,不再处于失效状 态)。这种做法是不是够变态啊 :-)
  为了防止发生这种诡异的事情,垃圾回收器只能在每次调用完finalize()之后再次去检查该对象是否还处于失效状态。这无形中又增加了JVM的开销。
  随便提一下。由于JDK的文档中规定了(具体见“这里 ”),JVM对于每一个类对象实例最多只会调用一次finalize()。所以,对于那些诈尸的实例,当它们真正死亡时,finalize()反而不会被调用了。这看起来是不是很奇怪?
  ◇要记得自己做异常捕获
  刚才在介绍finalize()调用机制时提到,一旦有异常抛出到finalize函数外面,会被垃圾回收线程捕获并丢弃。也就是说,异常被忽略掉了(异常被忽略的危害,“这里 ”有提到)。为了防止这种事儿,凡是finalize()中有可能抛出异常的代码,你都得写上try catch语句,自己进行捕获。
  ◇要小心线程安全
   由于调用finalize()的是垃圾回收线程,和你自己代码的线程不是同一个线程;甚至不同对象的finalize()可能会被不同的垃圾回收线程调 用(比如使用“并行收集器”的时候)。所以,当你在finalize()里面访问某些数据的时候,还得时刻留心线程安全的问题。

  ★结论
  前面废了这么多话,最后稍微总结一下。俺窃以为:finalize实在是Java的鸡肋。或许它对于极少数 程序员有用,但对于大多数人(包括俺自个儿),这玩意儿没啥明显的好处。大伙儿还是尽量不用为妙。


版权声明
本博客所有的原创文章,作者皆保留版权。转载必须包含本声明,保持本文完整,并以超链接形式注明作者编程随想 和本文原始地址:

http://program-think.blogspot.com/2009/06/java-performance-tuning-4-finalize.html

分享到:
评论

相关推荐

    Java面试宝典-经典

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    Java常见面试题208道.docx

    178.如何做 mysql 的性能优化? 十八、Redis 179.redis 是什么?都有哪些使用场景? 180.redis 有哪些功能? 181.redis 和 memecache 有什么区别? 182.redis 为什么是单线程的? 183.什么是缓存穿透?怎么解决? ...

    Java面试宝典2010版

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    java 面试题 总结

    JAVA相关基础知识 1、面向对象的特征有哪些方面 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用...

    java面试题大全(2012版)

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    java面试题

    84.9. 题目1:用1、2、2、3、4、5这六个数字,用java写一个main函数,打印出所有不同的排列,如:512234、412345等,要求:"4"不能在第三位,"3"与"5"不能相连. 117 84.10. 写一个方法,实现字符串的反转,如:输入...

    java面试宝典

    java面试试题 全面 准确 带答案 coreJava部分 8 1、面向对象的特征有哪些方面? 8 2、作用域public,private,protected,以及不写时的区别? 8 3、String 是最基本的数据类型吗? 8 4、float 型float f=3.4是否正确? 8 ...

    最新Java面试宝典pdf版

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    Java面试笔试资料大全

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    java面试宝典2012

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    JAVA面试宝典2010

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    Java面试宝典2012新版

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    Java面试宝典2012版

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    超级有影响力霸气的Java面试题大全文档

    超级有影响力的Java面试题大全文档 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。...

    千方百计笔试题大全

    coreJava部分 8 1、面向对象的特征有哪些方面? 8 2、作用域public,private,protected,以及不写时的区别? 8 3、String 是最基本的数据类型吗? 8 4、float 型float f=3.4是否正确? 8 5、语句float f=1.3;编译能否...

    Java 面试宝典

    4、在 JAVA 中如何跳出当前的多重嵌套循环? .......................................................... 8 5、switch 语句能否作用在 byte 上,能否作用在 long 上,能否作用在 String 上? .. 9 6、short s1 = ...

Global site tag (gtag.js) - Google Analytics