6

私はJavaファイナライザーに関するこれらのスライドを読んでいます。CleanResource.finalize()その中で、作成者は、別のスレッドで実行されている間にファイナライザースレッドによって実行される可能性があるシナリオ(スライド33)について説明してCleanResource.doSomething()います。これはどのように起こりますか?

doSomething()が非静的メソッドである場合、そのメソッドを誰かが実行するには、どこかでそれを強く参照する必要があります...そうですか?では、メソッドが戻る前に、この参照をどのようにクリアすることができますか?別のスレッドが急降下してその参照を無効にすることはできますか?それが起こった場合doSomething()でも、元のスレッドに正常に戻りますか?

私が本当に知りたいのはそれだけですが、本当に上を超えた答えについてはdoSomething()、スライド38がスライド29よりも優れている理由を教えてください。このメソッドdoSomething()を呼び出すだけで十分なのはなぜですか。keepAlive()呼び出し全体をmyImpl.doSomething()1つのsynchronized(this){}ブロックにまとめる必要はありませんか?

4

1 に答える 1

3

EDIT3:

結果として、ファイナライザーと通常のメソッドを同じインスタンスで同時に実行できます。これがどのように起こり得るかについての説明です。コードは基本的に次のとおりです。

class CleanResource {
   int myIndex;
   static ArrayList<ResourceImpl> all;

   void doSomething() {
     ResourceImpl impl = all.get(myIndex);
     impl.doSomething();
   } 

   protected void finalize() { ... }
}

このクライアントコードを考えると:

CleanResource resource = new CleanResource(...);
resource.doSomething();
resource = null; 

これは、この疑似CのようなものにJITされる可能性があります

register CleanResource* res = ...; call ctor etc..
// inline CleanResource.doSomething()
register int myIndex = res->MyIndex;
ResourceImpl* impl = all->get(myInddex);
impl->DoSomething();
// end of inline CleanResource.doSomething()
res = null;

このように実行するとres、インライン化CleanResource.doSomething()が完了するとクリアされるため、そのメソッドの実行が終了するまでgcは発生しません。同じインスタンス上の別のインスタンスメソッドと同時に実行を完了する可能性はありません。

ただし、resその時点以降は書き込み先は使用されません。フェンスがない場合は、実行の早い段階で書き込みの直後に移動できます。

register CleanResource* res = ...; call ctor etc..
// inline CleanResource->doSomething()
register int myIndex = res->MyIndex;
res = null;    /// <-----
ResourceImpl* impl = all->get(myInddex);
impl.DoSomething();
// end of inline CleanResource.doSomething()

マークされた場所(<---)には、CleanResourceインスタンスへの参照がないため、コレクションと呼び出されるファイナライザーメソッドの対象となります。最後の参照がクリアされた後はいつでもファイナライザーを呼び出すことができるため、ファイナライザーと残りの部分をCleanResource.doSomething()並行して実行することができます。

EDIT2:keepAlive()はthis、メソッドの最後でポインターにアクセスできるようにするため、コンパイラーはポインターの使用を最適化できません。また、このアクセスは指定された順序で発生することが保証されています(同期されたワードは、そのポイントの前後で読み取りと書き込みの並べ替えを許可しないフェンスをマークします)。

元の投稿:

この例では、doSomethingメソッドが呼び出され、一度呼び出されると、thisポインターを介して参照されるデータを早期に読み取ることができます(myIndex例では)。参照されたデータが読み取らthisれると、そのメソッドでポインターは不要になり、CPU /コンパイラーがレジスターを上書きするか、オブジェクトが到達不能であると宣言する可能性があります。したがって、GCは、オブジェクトのdoSomething()メソッドの実行と同時にファイナライザーを呼び出すことができます。

ただし、thisポインタが使用されていないため、これが具体的な効果をどのようにもたらすかを確認するのは困難です。

編集:まあ、おそらく、キャッシュを介してアクセスされているオブジェクトのフィールドへのキャッシュされたポインタがあり、thisそれが再利用される前から計算され、オブジェクトが再利用されると、メモリ参照は無効になります。これが可能だとは信じがたい部分もありますが、これもトリッキーなコーナーケースのようで、JSR-133にはデフォルトでこれを防ぐものはないと思います。オブジェクトがそのベースへのポインタによってのみ参照されていると見なされるのか、それともそのフィールドへのポインタによっても参照されていると見なされるのかが問題です。

于 2010-07-29T13:54:57.267 に答える