30

私は、あるオブジェクト「foo」が別のオブジェクト「bar」を作成し、Callable. この foo が bar を返すと、foo に到達できなくなります (つまり、ガベージ コレクションに使用できます)。

私の最初の考えは、Callable匿名で作成することでした。例えば:

class Foo {
  ...

  public Bar createBar() {
    final int arg1 = ...
    final int arg2 = ...
    final int arg3 = ...
    return new Callable<Baz>() {
      @Override
      public Baz call() {
        return new Baz(arg1, arg2, arg3);
      }
    };
  }
}

ただし、内部クラスは通常、それを囲むオブジェクトへの参照を保持するため、これは実際には期待どおりに機能しない可能性があることに気付きました。ここでは、囲んでいるクラスへの参照は必要ありません。これは、囲んでいるオブジェクトCallableがまだ到達可能な間に収集されるようにするためです。

一方、囲んでいるインスタンスが実際に参照されていないことを検出することは非常に簡単なはずなので、おそらく Java コンパイラはそのような場合に参照を含めないようにするのに十分スマートです。

では、匿名の内部クラスのインスタンスは、実際に囲んでいるインスタンス参照を使用していなくても、囲んでいるインスタンスへの参照を保持しますか?

4

3 に答える 3

31

はい、匿名の内部クラスのインスタンスは、それらの参照が実際に使用されていない場合でも、それらを囲むインスタンスへの参照を保持します。このコード:

public class Outer {
  public Runnable getRunnable() {
    return new Runnable() {
      public void run() {
        System.out.println("hello");
      }
    };
  }
}

でコンパイルするとjavac、2 つのクラス ファイルOuter.classOuter$1.class. 後者の匿名内部クラスを、yield で逆アセンブルしjavap -cます。

Compiled from "Outer.java"
class Outer$1 extends java.lang.Object implements java.lang.Runnable{
final Outer this$0;

Outer$1(Outer);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield        #1; //Field this$0:LOuter;
   5:   aload_0
   6:   invokespecial   #2; //Method java/lang/Object."<init>":()V
   9:   return

public void run();
  Code:
   0:   getstatic       #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #4; //String hello
   5:   invokevirtual   #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return

}

この行は、このフィールドが再び使用されることはないにもかかわらず、コンストラクターによって、putfield外側のインスタンスへの参照がthis$0( type の) フィールドに格納されていることを示しています。Outer

これは、(潜在的に大きな) 囲んでいるインスタンスを保持するため、匿名の内部クラスを持つ小さな潜在的に長命のオブジェクトを作成しようとしている場合は残念です。回避策は、代わりに静的クラス (または最上位クラス) のインスタンスを使用することです。残念ながら、これはより冗長です。

于 2011-02-20T00:07:59.537 に答える