[java] この参照をエスケープできるようにする


1 Answers

「エスケープ」とは、部分的に構築されたthisオブジェクトへの参照がシステム内の他のオブジェクトに渡される可能性があることを意味します。 このシナリオを考えてみましょう。

public Foo {
    public Foo() {
        setup();
    }

    protected void setup() {
       // do stuff
    }
}

public Bar extends Foo implements SomeListener {
    @Override protected void setup() {
        otherObject.addListener(this);
    }
}

問題は、新しいBarオブジェクトが、構築が完了する前にotherObjectに登録されていることです。 今、 otherObjectbarObjectでメソッドをbarObject 、フィールドが初期化されていないか、そうでなければbarObjectが矛盾した状態になる可能性があります。 barObjectへの参照( this自体)は、準備が整う前に残りのシステムに「エスケープ」されています。

代わりに、 setup()メソッドがFoofinal場合、 BarクラスはFooコンストラクタが終了する前にオブジェクトを表示させるコードをそこに配置できません。

Question

私は、「Java Concurrency in Practice」の次のことを理解する助けに感謝します:

オーバーライド可能なインスタンスメソッド(非公開でも最終的でもない)をコンストラクタから呼び出すと、この参照がエスケープできるようになります。

  1. ここでエスケープすると、インスタンスが完全に構築される前にインスタンスメソッドを呼び出している可能性があります。
    私は他の方法でインスタンスのスコープをエスケープする 'this'は表示されません。
  2. どのように '最終的に'これが起こるのを防ぐのですか?



セキュアなコーディング単位

BADコード:

final class Publisher {
  public static volatile Publisher published;
  int num;

  Publisher(int number) {
    published = this;
    // Initialization
    this.num = number;
    // ...
  }
}   

オブジェクトの初期化(したがってその構築)がコンストラクタ内のセキュリティチェックに依存する場合、信頼できない呼び出し元が部分的に初期化されたインスタンスを取得すると、セキュリティチェックをバイパスできます。 ルールOBJ11-J参照してください。 コンストラクタが例外をスローして詳細を通知することに注意してください。

final class Publisher {
  public static Publisher published;
  int num;

  Publisher(int number) {
    // Initialization
    this.num = number;
    // ...
    published = this;
  }
}

フィールドは不揮発性で非最終的なので、コンストラクタ内のステートメントは、初期化ステートメントが実行される前にこの参照が発行されるように、コンパイラによって並べ替えられます。

正しいコード

final class Publisher {
  static volatile Publisher published;
  int num;

  Publisher(int number) {
    // Initialization
    this.num = number;
    // ...
    published = this;
  }
}

このリファレンスは、現在のスコープを超えて利用可能になったときにエスケープされたと言われています。 この参照がエスケープできる一般的な方法は次のとおりです。

Returning this from a non-private, overridable method that is invoked from the constructor of a class whose object is being

構築された。 詳細については、MET05-Jのルールを参照してください。コンストラクタがオーバーライド可能なメソッドを呼び出さないようにします。これは、呼び出し元がオブジェクトの状態を間接的に操作できるようにする可変クラスの非プライベートメソッドからこれを返します。 これは、メソッド連鎖の実装でよく発生します。 規則VNA04-Jを参照のこと。 詳細については、チェーンメソッドの呼び出しがアトミックであることを確認してください。 これを、オブジェクトが構築されているクラスのコンストラクタから呼び出されたエイリアンメソッドへの引数として渡します。 内部クラスの使用。 内部クラスは静的宣言されていない限り、その外部クラスのインスタンスへの参照を暗黙的に保持します。 これはオブジェクトが構築されているクラスのコンストラクタからのパブリック静的変数に割り当てて公開します。 コンストラクタから例外をスローします。 そうすることで、ファイナライザの攻撃に対してコードが脆弱になる可能性があります。 規則OBJ11-Jを参照のこと。 コンストラクタが例外をスローして詳細を通知することに注意してください。 内部オブジェクトの状態をエイリアンメソッドに渡します。 これにより、このメソッドは内部メンバーオブジェクトのこの参照を取得できます。

このルールは、レースコンディションや不適切な初期化など、オブジェクトの構築中にこの参照をエスケープできるようにする潜在的な影響について説明しています。 たとえば、フィールドを最終的に宣言すると、通常、すべてのスレッドが完全に初期化された状態でフィールドを参照するようになります。 ただし、オブジェクトの構築中にこの参照をエスケープすると、フィールドが初期化されていないか、または部分的に初期化された状態の他のスレッドに公開される可能性があります。 ルールTSM03-J。 安全な公開のためのさまざまなメカニズムによって提供される保証を記述する、部分的に初期化されたオブジェクトをパブリッシュしないで、このルールに準拠していること。 したがって、プログラムは、オブジェクトの構築中にこの参照をエスケープすることを許可してはいけません。

一般に、この参照が現在のコンテキストの範囲外に漏れる可能性がある場合を検出することが重要です。 具体的には、公的変数と方法を注意深く精査する必要があります。






Related