`

Java中的finalize进行详解

 
阅读更多

程序员都了解初始化的重要性,但常常会忘记同样重要的清除工作。毕竟,谁需要清除一个int 呢?但在使用程序库时,把一个对象用完后就“弃之不顾”的做法并非总是安全的。当然,Java有垃圾回收器来回收无用对象占据的内存资源。但也有特殊情 况:假定你的对象(并非使用 new)获得了一块“特殊”的内存区域,由于垃圾回收器只知道释放那些经由 new分配的内存,所以它不知道该如何释放该对象的这块 “特殊”内存。为了应对这种情况,Java允许你在类中定义一个名为finalize( )的方法。它的工作原理“应该”是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其 finalize( )方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。所以要是你打算用 finalize( ),就能在“垃圾回收时刻”做一些重要的清除工作。

  这里有一个潜在的编程陷阱,因为有些程序员(特别是 C++程序员)刚开始可能会误把finalize( )当作C++中的“析构函数”(C++中销毁对象必须用到这个函数)。所以有必要明确区分一下:在 C++中,对象一定会被“销毁”(如果程序中没有错误的话);而 Java 里的对象却并非总是被“垃圾回收”的。或者换句话说:

  1. 对象可能不被回收。

  2. 垃圾回收并不等于“析构”。

  牢记这些,你就能远离困扰。这意味着在你不再需要某个对象之前,如果必须执行某些动作,那么你得自己去做。Java并未提供“析构函数”或相似 的概念,要做类似的清除工作,你必须自己动手创建一个执行清除工作的普通方法。例如,假设某个对象在创建过程中,会将自己绘制到屏幕上。要是你不明确地从 屏幕上将其擦除,它可能永远得不到清除。如果在

  finalize( )里加入某种擦除功能,当“垃圾回收”发生时(不能保证一定会发生),finalize( )得到了调用,图像就会被擦除。要是“垃圾回收”没有发生,图像就会一直保留下来。

  也许你会发现,只要程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得不到释放。如果程序执行结束,并且垃圾回收器一直都没有释放你创 建的任何对象的存储空间,则随着程序的退出,那些资源会全部交还给操作系统。这个策略是恰当的,因为垃圾回收本身也有开销,要是不使用它,那就不用支付这 部分开销了。

  finalize( )用途何在?

  此时,你已经明白了不该将finalize( )作为通用的清除方法。那么,finalize( )的真正用途是什么呢?

  这引出了要记住的第三点:

  3.垃圾回收只与内存有关。

  也就是说,垃圾回收器存在的唯一原因是为了回收程序不再使用的内存。所以对于与垃圾回收有关的任何行为来说(尤其是finalize( )方法),它们也必须同内存及其回收有关。

  但这是否意味着要是对象中含有其他对象,finalize( )就应该明确释放那些对象呢?不——无论对象是如何创建的,垃圾回收器都会负责释放对象占据的所有内存。这就将对 finalize( )的需求限制到特殊情况之下:你通过某种非“创建对象”的方式为对象分配了存储空间。不过,你也看到了,Java中一切皆为对象,那这种特殊情况是怎么回 事呢?

  看来之所以要有finalize( ),是由于你可能在分配内存时,采用了类似C语言中的做法而非Java中的通常做法。这种情况主要发生在使用“本地方法”的情况下,它是在Java中调用 非Java代码的一种方式。本地方法目前只支持C和C++。但它们可以调用其它语言写的代码,所以你实际上可以调用任何代码。在非Java代码中,也许会 调用类似C的malloc( )函数,用它分配存储空间,而且除非调用了free( )函数,否则存储空间将不会得到释放,从而造成内存泄露。当然,free( )是C和C++中的函数,所以你需要在finalize( )中用本地方法调用它。

  至此,你或许已经明白了不要过多地使用finalize( )的道理了。对,它确实不是进行普通的清除工作的合适场所。那么,普通的清除工作应该在哪执行呢?

  你必须执行清除

  为清除一个对象,用户必须在进行清除的时刻调用执行清除动作的方法。听起来似乎很简单,但却与C++中的“析构函数”的概念稍有抵触。在C++ 中,所有对象都会被销毁,或者说, “应该”被销毁。如果在C++中创建了一个局部对象(就是在堆栈上创建,Java中可不行),此时的销毁动作发生在以“右花括号”为边界的、此对象作用域 的末尾处进行。如果对象是

  用new创建的(类似于Java),那么当程序员调用C++的delete( )时(Java没有这个命令),就会调用相应的析构函数。如果程序员忘了,那么永远不会调用析构函数,就会出现内存泄露,对象的其他部分也不会得到清除。 这种错误很难跟踪,这也是让 C++程序员转向 Java的一个主要因素。

  相反,Java不允许创建局部对象,你必须使用new。在Java中,也没有“delete”来释放对象,因为垃圾回收器会帮助你释放存储空 间。甚至可以肤浅地认为,正是由于垃圾收集机制的存在,使得 Java 没有析构函数。然而,随着学习的深入,你就会明白垃圾回收器的存在并不能完全代替析构函数。(而且你绝对不能直接调用finalize( ),所以这也不是一个恰当的

  途径。)如果你希望进行除释放存储空间之外的清除工作,你还是得明确调用某个恰当的Java方法。这就等同于使用析构函数了,而且没有它方便。

  记住,无论是“垃圾回收”还是“终结”,都不保证一定会发生。如果Java虚拟机(JVM)并未面临内存耗尽的情形,它是不会浪费时间在回收垃圾以恢复内存上的。

  终结条件

  通常,你不能指望finalize( ),你必须创建其它的“清除”方法,并且明确地调用它们。看来,finalize( )只能存在于程序员很难用到的一些晦涩用法里了。不过,finalize( )还有一个有趣的用法,它并不依赖于每次都要对finalize( )进行调用,这就是对象“终结条件”的验证。

  当你对某个对象不再感兴趣,也就是它可以被清除时,这个对象应该处于某种状态,使它占用的内存可以被安全地释放。例如,要是对象代表了一个打开 的文件,在对象被回收前程序员应该关闭这个文件。只要对象中存在没有被适当清除的部分,你的程序就存在很隐晦的错误。finalize( )的价值在于可以用来最终发现这种情况,尽管它并不总是会被调用。如果某次

  finalize( )的动作使得bug被发现,那你就可据此找出问题所在——这才是你真正关心的。

  以下是个简单的例子,示范了可能的使用方式:

class Book {
  boolean checkedOut = false;
  Book(boolean checkOut) {
  checkedOut = checkOut;
  }
  void checkIn() {
  checkedOut = false;
  }
  public void finalize() {
  if(checkedOut)
  System.out.println("Error: checked out");
  // Normally, you'll also do this:
  // super.finalized();
  }
  }
  public class TerminationCondition {
  public static void main(String[] args) {
  Book novel = new Book(true);
  // Proper cleanup:
  novel.checkIn();
  // Drop the reference, forget to clean up:
  new Book(true);
  // Force garbage collection & finalization:
  System.gc();
  }
  }

 本例的终结条件是:所有的Book对象在被当作垃圾回收前都应该被签入(check in)。但在main( )方法中,由于程序员的错误,有一本书未被签入。要是没有 finalize( )来验证终结条件,将很难发现这种错误。

  注意,System.gc( )用于强制终结动作的进行。即使不这么做的话,通过重复的执行程序(假设程序将分配大量的存储空间而导致垃圾回收动作的执行),最终也能找出错误的Book对象。

分享到:
评论

相关推荐

    Java中finalize()详解及用法

    Java中finalize()详解及用法 在Java中,finalize()方法是一个重要的概念,它与final关键字息息相关。下面我们将深入探讨finalize()方法的用法和应用场景。 一、final关键字 final是Java的关键字,它所表示的是...

    详解Java编程中final,finalize,finally的区别

    当一个对象被垃圾回收器标记为即将回收时,Java会尝试调用该对象的`finalize`方法,提供一个机会进行必要的清理操作。然而,这个方法并不保证一定会被执行,因此不应该依赖它来清理资源。在实际编程中,`finalize`的...

    详解java中finalize的实现与相应的执行过程

    Java中的`finalize`方法是Object类的一个特殊方法,它的主要作用是在对象被垃圾收集器回收前执行清理工作。本文将详细解析`finalize`方法的实现和执行过程。 首先,了解`finalize`方法的用途:当一个对象不再被任何...

    java中关键字Object详解

    Java中的`Object`类是所有类的根,这意味着无论你定义的任何自定义类,如果没有显式地声明继承自其他类,那么它们都会隐式地继承`Object`类。`Object`类提供了基本的方法,这些方法是所有Java对象共有的。下面我们将...

    Java垃圾回收finalize()作用详解

    总的来说,Java的`finalize()`方法提供了一种在对象被垃圾收集器回收前进行额外操作的机会,但这应该谨慎使用,因为它的执行时间和效果都是不确定的。在设计程序时,优先考虑使用Java提供的自动资源管理机制,如`try...

    java虚拟机垃圾回收详解

    在Java中,`System.gc()`方法只是建议JVM进行垃圾回收,但是否实际执行取决于JVM的决定。此外,需要注意的是,对象的`finalize`方法的执行顺序是不确定的。尽管某些文档可能指出对象的`finalize`方法会按照特定的...

    Java.Bug模式详解

    Java编程语言虽然以其稳定性和强大的功能受到广大开发者喜爱,但正如...阅读"Java.Bug模式详解.pdf"这份资料,将有助于深入理解这些模式,并学习如何避免它们。持续学习和实践,是成为一名优秀的Java开发者的必经之路。

    Java中覆盖finalize()方法实例代码

    Java中的finalize()方法覆盖实例代码详解 finalize()方法是Java中的一个特殊方法,它可以在对象被垃圾回收器回收之前执行一次。这个方法可以用来释放对象持有的资源,但是它的使用需要非常小心,因为它带有很多不...

    Java垃圾回收详解

    当系统将要发生内存溢出异常前,将会把这些对象列进回收范围之中进行第二次回收。 - **弱引用(Weak Reference)**:仅当GC进行时,无论系统内存是否充足,都会回收弱引用的对象。 - **虚引用(Phantom Reference)*...

    java虚拟机垃圾回收详解.docx

    2. **Finalizable**:当GC发现一个对象不再可达(即没有其他对象引用它),这个对象会被放入一个名为Finalizer队列(F Queue)中,准备进行`finalize()`方法的执行。这是对象生命周期中的一个过渡阶段。 3. **...

    Java笔试面试题详解

    19. **Java无goto**:Java语言中没有goto关键字,鼓励使用结构化编程。 20. **length()方法**:数组有`length`属性而非`length()`方法,String有`length()`方法返回字符串长度。 21. **Overload与Override**:...

    java笔试题目详解

    - `finalize`:是一个特殊的方法,当垃圾收集器准备回收一个对象时,会先调用该对象的finalize()方法,供对象进行必要的清理工作。但这并不保证一定会执行,且不推荐用于资源清理,因为执行时机不确定。 2. **匿名...

    Java面试题,他会给你很好的Java面试准备,第一,谈谈final, finally, finalize的区别,第二,Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)?

    在Java面试中,了解final、finally、finalize的区别是非常重要的。final是一个修饰符(关键字),如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。将变量或方法声明为final,可以保证...

    2022年Java程序员面试题详解.docx

    Java程序员面试时,面试官通常会...以上问题涵盖了Java的基础语法、集合框架、异常处理、多线程、内存管理等多个核心知识点,是面试中常见的考察点。理解并熟练掌握这些内容,对于成为一名合格的Java程序员至关重要。

    Java C# 语法 比较

    - C#中的析构函数用`~`符号标记,但其功能与Java中的`finalize`方法不同。 - 两种语言都支持接口和抽象类的概念,但具体实现有差别。 - 泛型在Java中使用`<T>`语法,在C#中则更灵活,支持类型参数约束等特性。 7...

    java初学者必看 java初学者必看

    在Java中,`finalize()`方法是一种特殊的机制,用于对象被销毁前执行清理工作。不过,这个方法的使用并不推荐,主要有以下原因: - **不可预测性**:对象是否会被调用`finalize()`方法以及何时调用都是不确定的。 - ...

    2022常见JAVA面试题一.docx

    本文将对 Java 面试题进行详解,涵盖 final、finally、finalize、Anonymous Inner Class、Static Nested Class、Inner Class、& 和&&、HashMap 和 Hashtable、Collection 和 Collections 等知识点。 一...

    Java终止函数详解

    Java 终止函数详解 Java 终止函数是 Java 语言中的一种机制,用于在对象不再被需要时清除任务。它与 Java 结构函数相反,后者创建和初始化了一个 Java 类实例。Java 终止函数可以被用来清除任务,例如释放文件描述...

    Java面试题详解,和一些基础知识的深入剖析,个人认为非常棒

    为了在激烈的求职竞争中脱颖而出,Java程序员必须对基础知识有深入的理解,并能够应对各种面试中的问题。本资源集合了Java面试中常见的问题,涵盖了从基础概念到高级特性的广泛话题。 首先,我们来看“JAVA面试题...

Global site tag (gtag.js) - Google Analytics