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にはデフォルトでこれを防ぐものはないと思います。オブジェクトがそのベースへのポインタによってのみ参照されていると見なされるのか、それともそのフィールドへのポインタによっても参照されていると見なされるのかが問題です。