5

Java のメモリ モデルは、ルールを強制する「前に発生する」関係に基づいていますが、キャッシュの無効化に関して仮想マシンの実装を最適化することもできます。

たとえば、次の場合:

// thread A
private void method() {
   //code before lock
   synchronized (lockA) {
       //code inside
   }
}

// thread B
private void method2() {
   //code before lock
   synchronized (lockA) {
       //code inside
   }
}

// thread B
private void method3() {
   //code before lock
   synchronized (lockB) {
       //code inside
   }
}

スレッド A が呼び出され、スレッド B が内部でmethod()取得を試みた場合、ロックを解放する前にスレッド A がすべての変数に対して行ったすべての変更をスレッド B が監視する必要があります。ロック」セクション。lockAmethod2()lockA

一方、method3()別のロックを使用し、事前発生関係を強制しません。これにより、最適化の機会が生まれます。

私の質問は、仮想マシンがこれらの複雑なセマンティクスをどのように実装するかということです。キャッシュが不要な場合、キャッシュの完全なフラッシュを回避しますか?

どの変数がどの時点でどのスレッドによって変更されたかをどのように追跡して、必要なキャッシュラインだけをメモリからロードするのでしょうか?

4

2 に答える 2

1

どの変数がどの時点でどのスレッドによって変更されたかをどのように追跡して、必要なキャッシュラインだけをメモリからロードするのでしょうか?

いいえ、それは最新の CPU の仕組みではありません。

この種の問題が発生するほど複雑なマルチスレッド Java コードが実行されている可能性が高いすべてのプラットフォームでは、キャッシュ コヒーレンシがハードウェアに実装されています。キャッシュ ラインは、メイン メモリを経由せずに、あるキャッシュから別のキャッシュに直接転送できます。実際、あるコアに置かれて別のコアに取り込まれるたびに、データが低速のメイン メモリを通過する必要があるとしたら、それはひどいことです。したがって、キャッシュは互いに直接通信します。

コードがメモリ アドレスを変更すると、そのコアのキャッシュがそのメモリ アドレスの排他的所有権を取得します。別のコアがそのメモリ アドレスを読み取りたい場合、キャッシュは通常、直接通信によってメモリ アドレスを共有します。いずれかのコアが共有データを変更したい場合、もう一方のスレッドのキャッシュ内のデータを無効にする必要があります。

したがって、これらのキャッシュはハードウェアによって管理され、ソフトウェア レベルでは効果的に見えなくなります。

ただし、CPU にはプリフェッチまたはポステッド書き込みがある場合があります (まだキャッシュにはありません)。これらは単にメモリバリア命令を使用する必要があります。メモリ バリアは完全に CPU 内で動作し、バリアを越えたメモリ操作の並べ替え、遅延、または早期実行を防ぎます。CPU は、どのメモリ操作が遅延または実行されるかを事前に認識しているため、コードはそれを追跡する必要はありません。

于 2015-10-02T10:04:05.607 に答える