什么时候在Java中调用finalize()方法?
我需要知道什么时候在JVM
调用finalize()
方法。 我创build了一个testing类,当通过覆盖它来调用finalize()
方法时写入文件。 它没有执行。 有人可以告诉我为什么它不执行?
一般来说,最好不要依赖finalize()
来进行清理等等。
据Javadoc (这是值得一读),它是:
当垃圾收集确定没有更多的对象引用时,垃圾回收器调用对象。
正如Joachim指出的那样,如果对象总是可以访问的话,那么在程序的生命中就不会发生这种情况。
此外,垃圾收集器不保证在任何特定的时间运行。 一般来说,我想说的是finalize()
可能不是一般使用的最好的方法,除非有特定的东西需要它。
当一个对象即将被垃圾收集时, finalize
方法被调用。 这可以在它已经有资格进行垃圾收集之后。
请注意,一个对象永远不会被垃圾收集是完全可能的(因此finalize
永远不会被调用)。 当对象永远不符合gc的条件(因为它可以在JVM的整个生命周期内到达),或者在对象变为合格的时间和JVM停止运行的时间之间没有真正运行垃圾收集时,就可能发生这种情况testing程序)。
有一些方法可以告诉JVM运行在尚未被调用的对象上finalize
,但是使用它们也不是一个好主意(这种方法的保证也不是很强)。
如果你依靠finalize
你的应用程序的正确操作,那么你做错了什么。 finalize
只能用于清理(通常是非Java)的资源。 而这正是因为JVM并不能保证在任何对象上调用finalize
。
protected void finalize() throws Throwable {}
- 每个类都inheritancejava.lang.Object的
finalize()
方法- 该方法在垃圾收集器确定不存在对该对象的更多引用时被垃圾收集器调用
- Object finalize方法不执行任何操作,但可以被任何类覆盖
- 通常应该重写清理非Java资源,即closures文件
如果重写
finalize()
那么使用try-catch-finally语句并总是调用super.finalize()
是很好的编程习惯。 这是一个安全措施,以确保您不会无意中错过closures对象调用类使用的资源protected void finalize() throws Throwable { try { close(); // close open files } finally { super.finalize(); } }
垃圾收集期间由
finalize()
引发的任何exception都会暂停最终确定,否则将被忽略finalize()
永远不会在任何对象上运行多次
引自: http : //www.janeg.ca/scjp/gc/finalize.html
你也可以查看这篇文章:
- 对象定型和清理
Java finalize()
方法不是析构函数,不应该用来处理应用程序依赖的逻辑。 Java规范声明,不能保证在应用程序的生存期内调用finalize
方法。
你可能想要的是一个finally
和清理方法的结合,如下所示:
MyClass myObj; try { myObj = new MyClass(); // ... } finally { if (null != myObj) { myObj.cleanup(); } }
检查出Effective Java,第2版第27页。 项目7:避免终结器
终结者是不可预知的,往往是危险的,通常是不必要的。 永远不要在终结者中做任何时间关键的事情。 永远不要依赖终结器来更新关键持久状态。
要终止资源,请使用try-finally代替:
// try-finally block guarantees execution of termination method Foo foo = new Foo(...); try { // Do what must be done with foo ... } finally { foo.terminate(); // Explicit termination method }
什么时候在Java中调用
finalize()
方法?
在GC检测到对象不再可及之前,以及在实际回收对象使用的内存之前,将调用finalize方法。
-
如果一个对象永远不可达,
finalize()
永远不会被调用。 -
如果GC没有运行,那么
finalize()
可能永远不会被调用。 (通常,GC只在JVM决定可能有足够的垃圾才能使其值得运行时运行。) -
在GC确定特定对象不可达之前,可能需要多个GC周期。 (Java GC通常是“世代”收集器…)
-
一旦GC检测到一个对象不可访问和可终结,它就位于最终队列中。 终止通常与正常的GC不同步发生。
(JVM规范实际上允许 JVM 永远不能运行终结器,只要它不回收对象所使用的空间,以这种方式实现的JVM将被削弱/无用,但是这种行为是“允许的” 。)
结果是,依靠定稿来做必须在一个确定的时间框架内完成的事情是不明智的。 这是“最佳实践”,根本不使用它们。 应该有一个更好的(即更可靠的)方法去做任何你在finalize()
方法中要做的事情。
定稿的唯一合法用途是清理与应用程序代码丢失的对象相关的资源。 即使这样,你也应该尝试编写应用程序代码,以便它不会丢失对象。 (例如,使用Java 7 + try-with-resources确保始终调用close()
…)
我创build了一个testing类,当通过覆盖它来调用finalize()方法时写入文件。 它没有执行。 有人可以告诉我为什么它不执行?
这很难说,但有一些可能性:
- 该对象不是垃圾收集,因为它仍然可以访问。
- 该对象不是垃圾收集,因为GC在testing完成之前不运行。
- 该对象由GCfind,并由GC放在最终队列中,但在您的testing结束之前未完成完成。
由于JVM调用finalize()方法存在不确定性(不确定finalize()是否被执行),为了研究目的,观察调用finalize()时会发生什么的更好的方法是强制JVM通过System.gc()
命令调用垃圾回收。
具体来说,当对象不再使用时,调用finalize()。 但是,当我们试图通过创build新的对象来调用它时,就不能确定它的调用。 所以为了确定,我们创build了一个null
对象c
,显然没有将来的使用,因此我们看到了对象c
的最终调用。
例
class Car { int maxspeed; Car() { maxspeed = 70; } protected void finalize() { // Originally finalize method does nothing, but here we override finalize() saying it to print some stmt // Calling of finalize is uncertain. Difficult to observe so we force JVM to call it by System.gc(); GarbageCollection System.out.println("Called finalize method in class Car..."); } } class Bike { int maxspeed; Bike() { maxspeed = 50; } protected void finalize() { System.out.println("Called finalize method in class Bike..."); } } class Example { public static void main(String args[]) { Car c = new Car(); c = null; // if c weren`t null JVM wouldn't be certain it's cleared or not, null means has no future use or no longer in use hence clears it Bike b = new Bike(); System.gc(); // should clear c, but not b for (b.maxspeed = 1; b.maxspeed <= 70; b.maxspeed++) { System.out.print("\t" + b.maxspeed); if (b.maxspeed > 50) { System.out.println("Over Speed. Pls slow down."); } } } }
产量
Called finalize method in class Car... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51Over Speed. Pls slow down. 52Over Speed. Pls slow down. 53Over Speed. Pls slow down. 54Over Speed. Pls slow down. 55Over Speed. Pls slow down. 56Over Speed. Pls slow down. 57Over Speed. Pls slow down. 58Over Speed. Pls slow down. 59Over Speed. Pls slow down. 60Over Speed. Pls slow down. 61Over Speed. Pls slow down. 62Over Speed. Pls slow down. 63Over Speed. Pls slow down. 64Over Speed. Pls slow down. 65Over Speed. Pls slow down. 66Over Speed. Pls slow down. 67Over Speed. Pls slow down. 68Over Speed. Pls slow down. 69Over Speed. Pls slow down. 70Over Speed. Pls slow down.
注意 – 即使在打印多达70次之后,程序中没有使用对象b,也不能确定是否由JVM清除b,因为“自行车类中调用的最终处理方法”未打印。
敲定将打印出课程创作的计数。
protected void finalize() throws Throwable { System.out.println("Run F" ); if ( checkedOut) System.out.println("Error: Checked out"); System.out.println("Class Create Count: " + classCreate); }
主要
while ( true) { Book novel=new Book(true); //System.out.println(novel.checkedOut); //Runtime.getRuntime().runFinalization(); novel.checkIn(); new Book(true); //System.runFinalization(); System.gc();
如你看到的。 下面的输出显示当课程数为36时,第一次执行gc。
C:\javaCode\firstClass>java TerminationCondition Run F Error: Checked out Class Create Count: 36 Run F Error: Checked out Class Create Count: 48 Run F
最近,为了在testing期间处理连接池,最终与终结器方法进行了搏斗,但我不得不说,终结器缺乏很多东西。 使用VisualVM观察以及使用弱引用来跟踪实际的交互,我发现在Java 8环境(Oracle JDK,Ubuntu 15)中,以下情况是正确的:
- Finalize不立即调用Finalizer(GC部分)单独拥有引用
- 默认垃圾收集器池不可访问的对象
- Finalize被批量调用,指向垃圾收集器释放资源的某个阶段的实现细节。
- 调用System.gc()通常不会导致对象更频繁地结束,这只会导致Finalizer更快地意识到不可访问的对象
- 创build线程转储几乎总是会导致触发终结器,因为在执行堆转储或其他一些内部机制
- 最终化接缝受内存要求(释放更多内存)或被标记的对象列表限制,以便最终确定内部限制的增长。 所以,如果你有很多的对象完成定稿阶段将被触发得更多和更早,而只有less数比较
- 在某些情况下,System.gc()直接引发了一个finalize,但是只有当这个引用是一个本地和短暂的生活。 这可能与一代有关。
最后的想法
Finalize方法不可靠,但只能用于一件事情。 您可以确保在收集垃圾之前closures或丢弃了一个对象,以便在涉及生命周期结束的更复杂的生命周期的对象得到正确处理的情况下实现故障安全。 这是我能想到的一个原因,为了覆盖它,这是值得的。
finalize方法无法保证。当对象变为符合GC要求时调用此方法。 有很多情况下,物体可能不被垃圾收集。
如果对象无法从任何活动线程或任何静态引用进行访问,换句话说,如果某个对象的所有引用都为空,则可以说该对象变为符合垃圾回收的条件,则该对象就符合垃圾回收或GC的条件。 循环依赖不被视为引用,所以如果对象A具有对象B的引用,并且对象B具有对象A的引用,并且它们没有任何其他活动引用,则对象A和B都将有资格进行垃圾收集。 一般来说,一个对象在以下情况下有资格在Java中进行垃圾回收:
- 该对象的所有引用显式设置为null,例如object = null
- 对象是在一个块内创build的,一旦控件退出该块,引用就会超出范围。
- 父对象设置为空,如果一个对象持有另一个对象的引用,并且当你设置容器对象的引用为null,那么子对象或包含对象将自动变为垃圾收集的条件。
- 如果一个对象只有通过WeakHashMap的实时引用,它将有资格进行垃圾回收。
我们重写finalize方法的类
public class TestClass { public TestClass() { System.out.println("constructor"); } public void display() { System.out.println("display"); } @Override public void finalize() { System.out.println("destructor"); } }
确定方法被调用的机会
public class TestGarbageCollection { public static void main(String[] args) { while (true) { TestClass s = new TestClass(); s.display(); System.gc(); } } }
当内存与转储对象重载时,gc将调用finalize方法
运行并看到控制台,在那里你不会经常调用finalize方法,当内存越来越重,那么finalize方法将被调用。
有时当它被摧毁时,一个对象必须做出一个行动。 例如,如果某个对象具有非Java资源(如文件句柄或字体),则可以在销毁对象之前validation是否释放了这些资源。 为了pipe理这种情况,java提供了一个叫做“finalizing”的机制。 通过定义它,您可以定义在将要从垃圾收集器中移除对象时发生的特定操作。 要添加一个终结器到一个类,只需定义finalize()方法。 每当Java执行时间即将删除该类的一个对象时,就会调用这个方法。 在finalize 方法()中 ,指定要在销毁对象之前执行的操作。 垃圾收集器会定期search不再引用任何运行状态的对象,或间接引用任何其他对象。 在资产被释放之前,Java运行时调用对象上的finalize()方法。 finalize()方法具有以下一般forms:
protected void finalize(){ // This is where the finalization code is entered }
使用受保护的关键字,可以防止其类中的代码对finalize()的访问。 理解finalize()在垃圾回收之前就被调用是很重要的。 例如,当对象离开作用域时,不会调用它。 这意味着你不知道什么时候执行finalize() 。 因此,程序必须提供其他方法来释放系统资源或对象所使用的其他资源。 您不应该依赖finalize()来正常运行程序。
Java允许对象实现一个名为finalize()的方法,可能会被调用。
如果垃圾收集器试图收集对象,则调用finalize()方法。
如果垃圾收集器没有运行,该方法不会被调用。
如果垃圾收集器未能收集对象并尝试 再次 运行 ,则该方法不会在第二次调用。
在实践中,你不太可能在实际项目中使用它。
请记住,它可能不会被调用,它绝对不会被调用两次。 finalize()方法可以运行零次或一次。
在下面的代码中,当运行它时,finalize()方法不会产生输出,因为程序在需要运行垃圾回收器之前退出。
资源
尝试运行这个程序更好的理解
public class FinalizeTest { static { System.out.println(Runtime.getRuntime().freeMemory()); } public void run() { System.out.println("run"); System.out.println(Runtime.getRuntime().freeMemory()); } protected void finalize() throws Throwable { System.out.println("finalize"); while(true) break; } public static void main(String[] args) { for (int i = 0 ; i < 500000 ; i++ ) { new FinalizeTest().run(); } } }