1

Java の finalize() メソッドと Garbage Collection について、以下の質問があります。

  1. finalize() メソッドを強制する方法はありますか?
    また、JDK から次のメソッドが非推奨になった理由は何ですか?

    Runtime.runFinalizersOnExit(true);

  2. 上記の質問に加えて、記事http://javarevisited.blogspot.com/2011/04/garbage-collection-in-java.htmlを読んでいました。この中で、ガベージ コレクターが呼び出される前に finalize() が呼び出されると著者が述べているポイント 5 を参照してください。それで、このことは確実に起こりますか?ガベージ コレクター メソッドの実行前に finalize() メソッドが常に呼び出されるということですか?

  3. ヒープに大量のガベージがあるが、ガベージ コレクタが実行されていないとします。では、これは JVM の悪い動作または欠陥ではありませんか?

  4. また、ガベージ コレクションが行われなかったためにアプリケーションのパフォーマンスがどのように低下​​するのでしょうか。

4

2 に答える 2

0

このような理由から、ファイナライズを使用しないでください。与えられた答えに加えて、サンプルコードをいくつか示します。これは、プログラマーとして、多くの場合、ファイナライズが実行されることを確認できないことを証明しています。例 1 と 5 はほとんど問題にならないため、関係ありません。ただし、3、4、および 5 の場合もあります。

そしてsについて読んでくださいPhantomReference。ファイナライズに関する質問への回答は、PhantomReferences に言及しないと完全ではありません。それらをデーモン以外のスレッドで処理し、すべてが処理される前に JVM が終了しないようにすることができます。

package com.example.foo;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.List;

/**
 * Demonstration of failing finalize-implementations. Just run main, wait and see that it does not
 * actually print any messages other than "good bye!", even though there are 5
 * <code>System.out.println(...)</code> in the finalize-methods.
 * 
 * <p>
 * However, finalize is run even if the instance was not created properly (Exception in
 * constructor). This may lead to problems, as the resources to close might not even exist.
 * 
 * <p>
 * This answers this question: "Why is there no guarantee that {@link Object#finalize()} is run?"
 * 
 * <p>
 * If you want an answer to the question: "Then what can I do to properly use finalize()?" <br/>
 * The answer is: Don't! <br/>
 * Not just because of the problems presented here. There are actually more problems! <br/>
 * Here's a better alternative: http://claude-martin.ch/java-cleanup/
 */
public class SomeClass {

  /**
   * Finalize of Object is not invoked by some JVMs because it is known to be empty. This is
   * relevant if you thought you could use a tools that can intercept invocation of finalize (as in
   * aspect-oriented programming).
   */
  static final class Finalize1 {
    // no finalize
  }

  /** Class and method not final. */
  static class Finalize2 {
    @Override
    protected void finalize() throws Throwable {
      // not final -> extending class doesn't have to call this method.
      System.out.println("Finalize2");
    }
  }

  /**
   * Finalize allocates new data and fails. GC runs it but you have no guarantee that the JVM can
   * run it successfully.
   */
  static final class Finalize3 {
    @Override
    protected void finalize() throws Throwable, OutOfMemoryError {
      // What if memory is already running out?
      // Chances are high - why else would gc be running?
      // Then even a small array could fail:
      byte[] bytes = new byte[Integer.MAX_VALUE];
      // OutOfMemoryError was thrown!
      System.out.println("Finalize3 " + bytes[0]);
      // Also not run:
      super.finalize();
    }
  }

  static Finalize4 finalize4;

  /**
   * This is just to show that you, as the author of a class, can not prevent that an instance is
   * referenced from a static field. This is also true for all data structures that exist until the
   * JVM exits.
   */
  static final class Finalize4 {
    @Override
    protected void finalize() throws Throwable {
      System.out.println("Finalize4");
    }
  }

  /**
   * JVM could end abruptly. Again, you have no control over this when you write a class.
   */
  static final class Finalize5 {
    @Override
    protected void finalize() throws Throwable {
      System.out.println("Finalize5");
    }
  }

  /** Runs Garbage Collection. */
  static void gc(int n) {
    final List<byte[]> list = new LinkedList<>();
    try {
      while (true)
        list.add(new byte[1024 * 1024]);
    } catch (OutOfMemoryError e) {
      // Now GC is needed...
    }
    for (int i = 0; i < n; i++) {
      System.gc();
      try { // Give it time:
        Thread.sleep(100);
      } catch (InterruptedException e) {
      }
    }
  }

  public static void main(String[] args) {
    gc(0); // fills memory

    // 1) no finalize implemented:
    new Finalize1();

    // 2) no call to super.finalize():
    new Finalize2() {
      private OutputStream resource = new ByteArrayOutputStream();

      @Override
      protected void finalize() throws Throwable {
        // Not calling super.finalize() !
        this.resource.close();
      }
    };

    // 3) Failing finalize:
    new Finalize3();

    // 4) static reference:
    finalize4 = new Finalize4();

    // Now let's try to get rid of them:
    gc(20);

    // 5) No time:
    new Finalize5();
    System.out.println("good bye!");
    System.exit(0);
  }

}
于 2014-07-23T07:22:12.803 に答える
0

finalizeメソッドが実際に呼び出されることはないため、確実に実行する必要がある重要なクリーンアップ コードは別の場所に移動する必要があります。これはおそらく自分で管理する必要があります。いくつかのリソースを保持しているオブジェクトを使い終わったら、明示的にクリーンアップを行います (closeたとえば、メソッドを実装します)。

于 2014-03-13T08:55:20.677 に答える