当垃圾收集器认为没有指向对象实例的引用时,会在销毁该对象之前调用finalize()方法。该方法最常见的作用是确保释放实例占用的全部资源。java并不保证定时为对象实例调用该方法,甚至不保证方法会被调用,所以该方法不应该用于正常内存处理。
下文链接:http://www.softhouse.com.cn/news/show/194.html
在许多方面, Java 类似于 C++ 。 Java 的语法非常类似于 C++ , Java 有类、方法和数据成员; Java 的类有构造函数; Java 有异常处理。
但是,如果你使用过 C++ 会发现 Java 也丢掉一些可能是你熟悉的特性。这些特性之一就是析构函数。取代使用析构函数, Java 支持 finalize() 方法。
在本文中,我们将描述 finalize() 与 C++ 析构函数的区别。另外,我们将创建一个简单的 Applet 来演示 finalize() 是如何工作的。
最终的界限
与 Java 不同, C++ 支持局部对象(基于栈)和全局对象(基于堆)。因为这一双重支持, C++ 也提供了自动构造和析构,这导致了对构造函数和析构函数的调用,(对于堆对象)就是内存的分配和释放。
在 Java 中,所有对象都驻留在堆内存,因此局部对象就不存在。结果, Java 的设计者觉得不需要析构函数(象 C++ 中所实现的)。
取而代之, Java 定义了一个特殊的方法叫做 finalize() ,它提供了 C++ 析构函数的一些功能。但是, finalize() 并不完全与 C++ 的析构函数一样,并可以假设它会导致一系列的问题。 finalize() 方法作用的一个关键元素是 Java 的垃圾回收器。
垃圾回收器
在 C/C++ 、 Pascal 和其他几种多种用途的编程语言中,开发者有责任在内存管理上发挥积极的作用。例如,如果你为一个对象或数据结构分配了内存,那么当你不再使用它时必须释放掉该内存。
在 Java 中,当你创建一个对象时, Java 虚拟机( JVM )为该对象分配内存、调用构造函数并开始跟踪你使用的对象。当你停止使用一个对象(就是说,当没有对该对象有效的引用时), JVM 通过垃圾回收器将该对象标记为释放状态。
当垃圾回收器将要释放一个对象的内存时,它调用该对象的 finalize() 方法(如果该对象定义了此方法)。垃圾回收器以独立的低优先级的方式运行,只有当其他线程挂起等待该内存释放的情况出现时,它才开始运行释放对象的内存。(事实上,你可以调用 System.gc() 方法强制垃圾回收器来释放这些对象的内存。)
在以上的描述中,有一些重要的事情需要注意。首先,只有当垃圾回收器释放该对象的内存时,才会执行 finalize() 。如果在 Applet 或应用程序退出之前垃圾回收器没有释放内存,垃圾回收器将不会调用 finalize() 。
其次,除非垃圾回收器认为你的 Applet 或应用程序需要额外的内存,否则它不会试图释放不再使用的对象的内存。换句话说,这是完全可能的:一个 Applet 给少量的对象分配内存,没有造成严重的内存需求,于是垃圾回收器没有释放这些对象的内存就退出了。
显然,如果你为某个对象定义了 finalize() 方法, JVM 可能不会调用它,因为垃圾回收器不曾释放过那些对象的内存。调用 System.gc() 也不会起作用,因为它仅仅是给 JVM 一个建议而不是命令。
finalize() 有什么优点呢?
如果 finalize() 不是析构函数, JVM 不一定会调用它,你可能会疑惑它是否在任何情况下都有好处。事实上,在 Java 1.0 中它并没有太多的优点。
根据 Java 文档, finalize() 是一个用于释放非 Java 资源的方法。但是, JVM 有很大的可能不调用对象的 finalize() 方法,因此很难证明使用该方法释放资源是有效的。
Java 1.1 通过提供一个 System.runFinalizersOnExit() 方法部分地解决了这个问题。(不要将这个方法与 Java 1.0 中的 System.runFinalizations() 方法相混淆。)不象 System.gc() 方法那样, System.runFinalizersOnExit() 方法并不立即试图启动垃圾回收器。而是当应用程序或 Applet 退出时,它调用每个对象的 finalize() 方法。
正如你可能猜测的那样,通过调用 System.runFinalizersOnExit() 方法强制垃圾回收器清除所有独立对象的内存,当清除代码执行时可能会引起明显的延迟。现在建立一个示例 Applet 来演示 Java 垃圾回收器和 finalize() 方法是如何相互作用的。