是否有Java的析构函数?我似乎无法找到任何关于此的文档.如果没有,我怎样才能达到同样的效果?
为了使我的问题更具体,我正在编写一个处理数据的应用程序,并且规范说应该有一个"重置"按钮,使应用程序恢复到其原始的刚启动状态.但是,除非关闭应用程序或按下重置按钮,否则所有数据都必须为"实时".
作为一名C/C++程序员,我认为实现这一点非常简单.(因此我计划最后实现它.)我构建了我的程序,使得所有'可重置'对象都在同一个类中,这样我就可以在按下重置按钮时销毁所有'实时'对象.
我在想如果我所做的只是取消引用数据并等待垃圾收集器收集它们,如果我的用户反复输入数据并按下重置按钮,是否会出现内存泄漏?我也在想,因为Java作为一种语言非常成熟,应该有办法防止这种情况发生或优雅地解决这个问题.
因为Java是一种垃圾收集语言,所以无法预测何时(或者甚至是)对象将被销毁.因此,没有直接等价的析构函数.
有一个名为的继承方法finalize
,但这完全由垃圾收集器决定.因此,对于需要明确整理的类,约定是定义一个close方法并仅使用finalize进行健全性检查(即,如果没有调用close,请立即执行并记录错误).
最近有一个问题引发了对最终定稿的深入讨论,因此如果需要,应提供更多深度......
如果您使用Java 7,请查看try-with-resources语句.例如:
try (BufferedReader br = new BufferedReader(new FileReader(path))) { System.out.println(br.readLine()); } catch (Exception e) { ... } finally { ... }
这里不再需要的资源在BufferedReader.close()
方法中被释放.您可以创建自己的类,AutoCloseable
以类似的方式实现和使用它.
此语句比finalize
代码结构更有限,但同时它使代码更易于理解和维护.此外,无法保证finalize
在应用程序的实时时间内完全调用方法.
不,这里没有破坏者.原因是所有Java对象都是堆分配和垃圾回收.没有显式的释放(即C++的删除运算符),没有合理的方法来实现真正的析构函数.
Java确实支持终结器,但它们仅用作对象,用于保存对套接字,文件句柄,窗口句柄等本机资源句柄的对象.当垃圾收集器收集没有终结器的对象时,它只是标记内存区域是免费的,就是这样.当对象有一个终结器时,它首先被复制到一个临时位置(记住,我们这里是垃圾收集),然后它被排队到一个等待最终确定的队列中,然后一个终结器线程以非常低的优先级轮询队列并运行终结器.
当应用程序退出时,JVM会在不等待挂起的对象完成的情况下停止,因此几乎不能保证终结器能够运行.
应避免使用finalize()方法.它们不是用于资源清理的可靠机制,并且可能通过滥用它们而在垃圾收集器中引起问题.
如果您需要在对象中进行释放调用,比如释放资源,请使用显式方法调用.这个约定可以在现有的API中看到(例如,Closeable,Graphics.dispose(),Widget.dispose()),通常通过try/finally调用.
Resource r = new Resource(); try { //work } finally { r.dispose(); }
尝试使用已处置的对象应该抛出运行时异常(请参阅IllegalStateException).
编辑:
我在想,如果我所做的只是取消引用数据并等待垃圾收集器收集它们,如果我的用户反复输入数据并按下重置按钮,是否会出现内存泄漏?
通常,您需要做的就是取消引用对象 - 至少,这是它应该工作的方式.如果您担心垃圾收集,请查看Java SE 6 HotSpot [tm]虚拟机垃圾收集调整(或JVM版本的等效文档).
随着Java 1.7的发布,您现在可以使用该try-with-resources
块的附加选项.例如,
public class Closeable implements AutoCloseable { @Override public void close() { System.out.println("closing..."); } public static void main(String[] args) { try (Closeable c = new Closeable()) { System.out.println("trying..."); throw new Exception("throwing..."); } catch (Exception e) { System.out.println("catching..."); } finally { System.out.println("finalizing..."); } } }
如果执行此类,c.close()
将在try
块保留时执行,并在执行catch
和finally
块之前执行.与finalize()
方法的情况不同,close()
保证执行.但是,不需要在finally
子句中明确地执行它.
我完全同意其他答案,说不要依赖于finalize的执行.
除了try-catch-finally块之外,您还可以使用Runtime#addShutdownHook(在Java 1.6中引入)在程序中执行最终清理.
这与析构函数不同,但是可以实现一个具有注册侦听器对象的关闭钩子,可以调用清理方法(关闭持久数据库连接,删除文件锁等) - 通常可以在破坏者.再次 - 这不是构造函数的替代,但在某些情况下,您可以使用此方法来实现所需的功能.
这样做的好处是解构行为与程序的其余部分松散耦合.
不,java.lang.Object#finalize
是你能得到的最接近的.
但是,当它(和如果)被调用时,不能保证.
看到:java.lang.Runtime#runFinalizersOnExit(boolean)
首先,请注意,由于Java是垃圾收集的,因此很少需要对对象销毁做任何事情.首先是因为你通常没有任何托管资源可供免费使用,其次是因为你无法预测何时或是否会发生这种情况,因此只要没有人再使用我的对象,就不适合你需要发生的事情. ".
使用java.lang.ref.PhantomReference销毁一个对象后可以得到通知(实际上,说它已经被销毁可能稍微不准确,但是如果它的幻像引用排队那么它就不再可恢复了,这通常相当于同一件事情).一个常见的用途是:
将类中需要被破坏的资源分离到另一个辅助对象中(请注意,如果您正在做的只是关闭连接,这是一种常见情况,您不需要编写新类:要关闭的连接将是该情况下的"辅助对象").
创建主对象时,还要为其创建一个PhantomReference.要么指向新的辅助对象,要么将PhantomReference对象的映射设置为相应的辅助对象.
在收集主对象之后,PhantomReference会排队(或者它可能会排队 - 就像终结器一样,不能保证它会一直存在,例如,如果VM退出则它不会等待).确保您正在处理其队列(在特殊线程中或不时).由于对辅助对象的硬引用,尚未收集辅助对象.因此,在帮助对象上执行任何清理操作,然后丢弃PhantomReference,最终也会收集帮助程序.
还有finalize(),它看起来像一个析构函数,但不像一个.这通常不是一个好选择.
如果这偏离主题,我很抱歉,但java.util.Timer(SE6)文档说:
"在对Timer对象的最后一次实时引用消失并且所有未完成的任务都已完成执行之后,定时器的任务执行线程正常终止(并且变为垃圾收集).但是,这可能会花费任意长的时间.默认情况下,任务执行线程不作为守护程序线程运行,因此它能够阻止应用程序终止.如果调用者想要快速终止计时器的任务执行线程,调用者应该调用计时器的取消方法......"
我想取决于拥有Timer失去其最后一个引用(或之前的immeditalesky)的类.在这里,一个可靠的析构函数可以为我做到这一点.上面的评论表明,最终是一个糟糕的选择,但有一个优雅的解决方案吗?"......能够阻止申请终止......"的业务并不吸引人.
该finalize()
函数是析构函数。
但是,通常不应该使用它,因为它是在GC之后调用的,您无法确定何时会发生(如果有的话)。
此外,要取消分配具有的对象还需要多个GC finalize()
。
您应该尝试使用try{...} finally{...}
语句清除代码中逻辑上的位置!
我同意大多数答案。
您不应完全依赖finalize
或ShutdownHook
定案
JVM不保证何时finalize()
调用此方法。
finalize()
仅由GC线程调用一次。如果对象从finalization方法中恢复过来,则finalize
不会再次调用。
在您的应用程序中,您可能有一些活动对象,这些活动对象从未调用过垃圾回收。
Exception
由finalize方法抛出的任何内容都将被GC线程忽略
System.runFinalization(true)
和Runtime.getRuntime().runFinalization(true)
方法增加了调用finalize()
方法的可能性,但是现在不赞成使用这两种方法。由于缺乏线程安全性和可能产生死锁,这些方法非常危险。
shutdownHooks
public void addShutdownHook(Thread hook)
注册一个新的虚拟机关闭挂钩。
Java虚拟机将响应以下两种事件而关闭:
当最后一个非守护线程退出时System.exit
,或者调用exit(等效于)方法时,程序将正常退出,或者
响应于用户中断(例如键入^ C)或系统范围的事件(例如用户注销或系统关闭)来终止虚拟机。
关闭钩子只是一个已初始化但未启动的线程。当虚拟机开始其关闭序列时,它将以未指定的顺序启动所有已注册的关闭挂钩,并使其同时运行。当所有挂钩完成后,如果启用了退出时终结,则它将运行所有未调用的终结器。
最后,虚拟机将停止。请注意,如果关闭是通过调用exit方法启动的,则守护程序线程将在关闭序列期间继续运行,非守护程序线程也将继续运行。
关机挂钩也应迅速完成工作。程序调用exit时,期望虚拟机将立即关闭并退出。
但是,即使Oracle文档引用了
在极少数情况下,虚拟机可能会中止,即在不干净关闭的情况下停止运行
当虚拟机在外部终止时,例如SIGKILL
在Unix 上的信号或TerminateProcess
在Microsoft Windows上的调用,会发生这种情况。如果本机方法出错(例如,破坏内部数据结构或尝试访问不存在的内存),则虚拟机也可能中止。如果虚拟机中止,则无法保证是否将运行任何关闭挂钩。
结论:适当地使用try{} catch{} finally{}
块并在finally(}
块中释放关键资源 。在finally{}
块释放资源期间,捕获Exception
和捕获Throwable
。