Java有析构函数吗?我好像找不到任何关于这个的文件。如果没有,我怎样才能达到同样的效果?
为了使我的问题更具体,我正在编写一个处理数据的应用程序,规范说应该有一个“重置”按钮,使应用程序恢复到最初的启动状态。但是,除非应用程序关闭或按下重置按钮,否则所有数据必须是“活的”。
作为一个通常的C/ c++程序员,我认为这是微不足道的实现。(因此我打算最后实现它。)我构造了我的程序,使所有“可重置”的对象都在同一个类中,这样当按下重置按钮时,我就可以销毁所有“活动”对象。
我在想,如果我所做的只是解除对数据的引用,并等待垃圾收集器收集它们,如果我的用户重复输入数据并按下重置按钮,是否会出现内存泄漏?我还在想,既然Java作为一种语言相当成熟,应该有一种方法来防止这种情况发生或优雅地解决这个问题。
首先,请注意,由于Java是垃圾收集的,因此很少需要对对象销毁做任何事情。首先是因为你通常没有任何托管资源可以释放,其次是因为你无法预测它何时或是否会发生,所以当你需要“一旦没有人再使用我的对象”就发生的事情是不合适的。
你可以使用java.lang.ref.PhantomReference在一个对象被销毁后得到通知(实际上,说它已经被销毁可能有点不准确,但如果对它的幻影引用处于队列中,那么它就不再是可恢复的,这通常是一样的)。常见的用法是:
Separate out the resource(s) in your class that need to be destructed into another helper object (note that if all you're doing is closing a connection, which is a common case, you don't need to write a new class: the connection to be closed would be the "helper object" in that case).
When you create your main object, create also a PhantomReference to it. Either have this refer to the new helper object, or set up a map from PhantomReference objects to their corresponding helper objects.
After the main object is collected, the PhantomReference is queued (or rather it may be queued - like finalizers there is no guarantee it ever will be, for example if the VM exits then it won't wait). Make sure you're processing its queue (either in a special thread or from time to time). Because of the hard reference to the helper object, the helper object has not yet been collected. So do whatever cleanup you like on the helper object, then discard the PhantomReference and the helper will eventually be collected too.
还有finalize(),它看起来像析构函数,但行为并不像析构函数。这通常不是一个好的选择。
随着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...");
}
}
}
如果执行这个类,则在try块剩余时,在catch块和finally块执行之前,将执行c.close()。与finalize()方法不同,close()保证被执行。但是,不需要在finally子句中显式地执行它。
我同意大部分答案。
你不应该完全依赖finalize或ShutdownHook
完成
The JVM does not guarantee when this finalize() method will be invoked.
finalize() gets called only once by GC thread. If an object revives itself from finalizing method, then finalize will not be called again.
In your application, you may have some live objects, on which garbage collection is never invoked.
Any Exception that is thrown by the finalizing method is ignored by the GC thread
System.runFinalization(true) and Runtime.getRuntime().runFinalization(true) methods increase the probability of invoking finalize() method but now these two methods have been deprecated. These methods are very dangerous due to lack of thread safety and possible deadlock creation.
shutdownHooks
public void addShutdownHook(Thread hook)
注册一个新的虚拟机关闭钩子。
Java虚拟机在响应两种事件时关闭:
The program exits normally, when the last non-daemon thread exits or when the exit (equivalently, System.exit) method is invoked, or
The virtual machine is terminated in response to a user interrupt, such as typing ^C, or a system-wide event, such as user logoff or system shutdown.
A shutdown hook is simply an initialized but non-started thread. When the virtual machine begins its shutdown sequence it will start all registered shutdown hooks in some unspecified order and let them run concurrently. When all the hooks have finished it will then run all uninvoked finalizers if finalization-on-exit has been enabled.
Finally, the virtual machine will halt. Note that daemon threads will continue to run during the shutdown sequence, as will non-daemon threads if the shutdown was initiated by invoking the exit method.
Shutdown hooks should also finish their work quickly. When a program invokes exit the expectation is that the virtual machine will promptly shut down and exit.
But even Oracle documentation quoted that
在极少数情况下,虚拟机可能会中止,即停止运行而不完全关闭
这发生在虚拟机从外部终止时,例如在Unix上使用SIGKILL信号或在Microsoft Windows上使用TerminateProcess调用。如果本机方法出错,虚拟机也可能中止,例如破坏内部数据结构或试图访问不存在的内存。如果虚拟机中止,则不能保证是否会运行任何shutdown钩子。
结论:合理使用try{} catch{} finally{}块,释放finally(}块中的关键资源。在finally{}块中释放资源时,捕获Exception和Throwable。