思诚科技 seecen LOGO
咨询热线:0791-87557233
  首页 |   Java EE  
关于思诚
关注官方微信

怎样处理Java最终化的内存保留问题

来源:网络    更新时间:2014-12-2


  最终化允许实现Java对象的最后清理;但是,就算不显式地使用它,它也能延迟资源的回收。在本文中你还会学习如何避免类似的内存保留问题。

  一、引言

  最终化是Java编程语言的一个特性:它允许你对垃圾收集器发现的不可达的对象进行最后清理。典型地用于回收与一对象相关联的本地资源。下面是一个简单的最终化示例:

publicclassImage1{
 //指向本地图像数据
 privateintnativeImg;
 privatePointpos;
 privateDimensiondim;
 //它释放本地图像;
 //随后对它的调用将被忽略
 privatenativevoiddisposeNative();
 publicvoiddispose(){disposeNative();}
 protectedvoidfinalize(){dispose();}
 staticprivateImage1randomImg;
}

图1.一个可最终化的对象
  有时,在一个Image1实例变为不可达的时,Java虚拟机(JVM)将调用它的finalize()方法来确保含有图像数据(在本例中被整数nativeImg所指向)的本地资源已经被回收。然而,请注意,该finalize()方法,先不管它被JVM进行专门对待,是一个任意的方法-它包含任意的代码。特别地,它能存取任何对象中的任何字段(在本例中指pos和dim)。令人惊讶的是,它还能使该对象再次成为可达的-比如说通过让它从一个静态字段成为可达的(如,randomImg=this;)。我根本不推荐使用后面这种编程实践,但是遗憾的是,Java编程语言允许它。

  下面步骤描述一个可最终化的对象obj的生命周期-也即,这是一个其类中有一个非平凡的(non-trivial)终结器的对象(见图1):


图2.垃圾收集器确定这个obj是不可达的
  1.当obj被分配时,JVM内部地记录下这个obj是可最终化的(这在典型情况下会减慢现代JVM具有的其它方面的分配路径)。

  2.当垃圾收集器确定该obj是不可达的时,它注意到,这个obj是可最终化的(因为它在分配时就被记录下来)并且把它添加到JVM的最终化队列上。它还确保从obj可达的所有对象被保留起来,即使它们从其它对象也许是不可达的,因为它们可能会被终结器所存取。图2展示了Image1的一个实例的情况。

  3.在后面的时候,JVM的终结器线程将出队obj,调用它的finalize()方法,并且记录下该obj的终结器已经被调用。此时,obj被认为是被最终化的。

  4.当垃圾收集器再次发现该obj是不可达的时,它将连同一切它所可达的(假定后者是不可达的)对象回收它的空间。

  注意,垃圾收集器至少需要两个周期(也许更多)来回收obj并且需要保留在该过程中所有另外的从obj可达的对象。如果一个程序员不小心,那么这可能会创建暂时的、微妙的和无法预言的资源保留问题。另外,JVM并不保证它将调用所有的已分配的可最终化的对象的终结器;它可能在垃圾收集器发现它们其中一些是不可达的之前就已退出。

二、在子类化时避免内存保留问题

  就算你不显式地使用它,最终化也可能延期资源的回收。请考虑下列实例:

publicclassRGBImage1extendsImage1{
 privatebytergbData[];
}
  RGBImage1扩展Image1并且引入了新字段rgbData(也许还有一些方法,而本示例中却没有显示)。尽管你没有显式地在RGBImage1上定义一终结器,但是,这个类将自然地继承Image1的finalize()方法,并且所有的RGBImage1实例也将被认为是可最终化的。当一个RGBImage1实例成为不可达的,回收可能的很大的rgbData数组将被延迟直到该实例被终结(见图3)。这可能是一个很难发现的问题,因为该终结器可能是隐藏在一个很深的类层次上。

  一种避免这个问题的方法是重新安排代码,这样它可以使用"包含"来代替"扩展"模式,如下所示:

  • 上一篇文章:

  • 下一篇文章:
  •  

    0791-87557233

    重视每个来电 珍惜您的时间
    思诚者开发沙龙
    江西思诚科技有限公司  赣ICP备17006097号  CopyRight©2014 - 2018