上次的帖子
聊了垃圾回收器的调优,当时啰嗦了比较长的篇幅,就没再继续提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性能优化关键知识点 #### 一、合理运用单例模式 **单例模式**是一种常用的软件设计模式,它确保一个类只有一个实例,并提供一个全局访问点。在Java性能优化中,合理使用单例模式可以带来以下优势: 1. **...
### Java性能优化借鉴 #### 一、合理使用单例模式 单例模式是软件开发中常用的模式之一,它确保一个类只有一个实例,并提供一个全局访问点。在Java中使用单例模式可以有效控制资源的使用,减少不必要的实例化操作...
以下是一些关键的性能优化细节: 1. **使用单例模式**:单例模式可以确保一个类只有一个实例,从而节省资源和提高效率。不过,过度使用单例可能导致设计复杂性和测试困难,因此需谨慎使用。例如,`java.lang....
在Java编程中,性能优化是提升应用程序效率的关键环节。这篇博文中,我们将深入探讨Java程序性能优化的二十三个建议,这些策略可以帮助我们编写出更高效、资源利用率更高的代码。以下是一些主要的优化点: 1. **...
在Java开发中,适当地使用单例模式可以帮助优化性能: 1. **资源控制**:通过线程同步控制资源的并发访问,有效管理有限的资源。 2. **实例控制**:减少对象实例的创建,节约内存资源。 3. **数据共享**:使得不...
以下是对标题和描述中提到的26个性能优化技巧的详细说明: 1. **单例模式的使用**:单例模式可以减少资源创建,提高效率,适用于控制资源访问、限制实例数量和跨线程/进程通信。但需谨慎使用,避免过度使用导致设计...
根据提供的信息,我们可以总结出以下关于Java性能优化的关键知识点: ### Java性能优化概述 #### 标题:Java性能优化 #### 描述: 本篇内容强调了Java性能的重要性,并通过重复关键词“Java性能”来强调其核心...
下面是一些关键点,旨在优化Java程序的性能: 1. **单例模式的使用**:单例模式可以减少对象的创建,节省资源,尤其适用于控制资源访问、限制实例创建和实现跨线程/进程通信的情况。但要注意,过度使用或不恰当的...
在Java编程中,提升代码性能是一项关键任务,它涉及到如何有效地利用系统资源,减少不必要的开销,以及优化程序运行效率。以下是一些针对Java代码性能的建议: 1. **使用单例模式**:单例模式可以减少对象创建,...
通过上述对JVM内存区域划分、JVM执行子系统、垃圾回收器和内存分配策略、编写高效优雅Java程序以及性能优化等方面的详细介绍,我们可以更好地理解和掌握Java程序的运行机制,进而提高程序的性能和质量。
Java中的`finalize()`方法是对象生命周期的一部分,它与C++中的析构函数类似,但在功能和调用时机上有所不同。`finalize()`方法是在对象被GC标记为可回收并准备释放之前调用的,提供了在对象被彻底删除前执行某些...
在Java编程中,性能优化是提升程序运行效率的关键。下面将详细介绍标题和描述中提到的26个要点,帮助开发者养成良好的编程习惯,提高代码性能。 1. **使用单例模式**:在需要控制资源访问、节约内存或实现跨线程...
了解GC的工作原理对于优化Java程序至关重要,尤其是在那些对性能有着极高要求的应用场景中,如嵌入式系统或实时系统。下面我们将详细介绍GC的基础知识。 Java内存管理主要涉及对象的分配与释放。当创建新对象时,...
- **构造函数与析构函数**:了解构造函数的作用,何时调用,以及析构函数的概念(Java中没有标准的析构函数,但可以使用 finalize 方法)。 - **访问修饰符**:public、private、protected以及默认修饰符的权限...
- **3.1.14 Java没有sizeof操作符**:关于sizeof的讨论。 - **3.1.15 表达式的顺序**:表达式求值顺序的重要性。 - **3.1.16 小结**:本章内容回顾。 - **3.2 控制流**: - **3.2.1 图解**:使用图形来解释控制...
### Java编程中“为了性能”尽量要做到的一些地方 #### 1. 尽量在合适的场合使用单例 在Java编程中,单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。正确使用单例模式可以在...
在Java编程中,优化代码和遵循良好的编码规范对于提高程序的运行效率至关重要。以下是一些关键的优化技巧和规范: 1. **使用单例模式**:单例模式能确保一个类只有一个实例,常用于控制资源的访问、节省内存以及跨...