Javaメモリモデルは、Javaプログラミング言語におけるスレッドがメモリを介してどのように相互作用するかを記述します。メモリモデルは、シングルスレッドによるコード実行の記述と合わせて、Javaプログラミング言語の セマンティクスを提供します。
1995 年に開発されたオリジナルの Java メモリ モデルは、多くのランタイム最適化を妨げ、コードの安全性に対する十分な保証を提供しないという点で、欠陥があると広く認識されていました[ 1 ]。これは、Java Community Process を通じて、 Tiger ( Java 5.0)向けに 2004 年に発効した Java Specification Request 133 (JSR-133) として更新されました。[ 2 ] [ 3 ]
Javaプログラミング言語とプラットフォームは、スレッド機能を提供します。スレッド間の同期は開発者にとって非常に難しいことで知られていますが、Javaアプリケーションは幅広いプロセッサとオペレーティングシステム上で動作できるため、この難しさはさらに増します。プログラムの動作について結論を導き出すために、Javaの設計者はすべてのJavaプログラムの可能な動作を明確に定義する必要があると判断しました。
現代のプラットフォームでは、コードは必ずしも記述された順序で実行されるとは限りません。コンパイラ、プロセッサ、メモリサブシステムによって、最高のパフォーマンスを実現するためにコードの順序が再編成されます。マルチプロセッサアーキテクチャでは、個々のプロセッサがメインメモリと同期していない独自のローカルキャッシュを持つ場合があります。スレッドが互いに完全に同期していることを要求することは、パフォーマンスの観点からコストがかかりすぎるため、一般的には望ましくありません。その結果、異なるスレッドが、特定の時点で同じ共有データの異なる値を参照する可能性があります。[ 4 ]
シングルスレッド環境では、コード実行の仕組みを理解するのは容易です。一般的なアプローチでは、システムが個々のスレッドに対して、あたかも逐次実行であるかのように動作するセマンティクスを個別に実装する必要があります。個々のスレッドが実行されるとき、そのスレッドによって実行されるすべてのアクションは、プログラムに記述されている順序で発生したかのように見えます。たとえ実際のアクションが順不同で実行されていてもです。
あるスレッドが命令を順不同で実行した場合、たとえそれが最初のスレッドのセマンティクスに影響を与えなかったとしても、別のスレッドはそれらの命令が順不同で実行されたという事実を認識する可能性があります。たとえば、変数 x と y が両方とも 0 に初期化された状態で、次の命令を持つ 2 つのスレッドが同時に実行される場合を考えてみましょう。
順序変更が行われず、スレッド2でのyの読み取りで 値 2が返された場合、xへの書き込みがyへの書き込みより前に行われたため、xの読み取りでは値 1が返されるはずです。しかし、2つの書き込みの順序が変更された場合、yの読み取りでは値2が返され 、xの読み取りでは値 0が返される可能性があります。
Javaメモリモデル(JMM)は、マルチスレッドプログラムの許容される動作を定義し、それによって、そのような再順序付けが可能な場合を記述します。JMMは、一貫性と信頼性の高いJavaアプリケーションを実現するために、スレッドとメインメモリの関係に実行時制約を設けます。これにより、動的コンパイラ、プロセッサ、キャッシュによる最適化が行われた場合でも、マルチスレッド環境におけるコード実行について推論することが可能になります。
単一スレッドの実行に関しては、ルールは単純です。Java言語仕様では、Java仮想マシンがスレッド内ではあたかも直列であるかのように動作することを要求しています。ランタイム(この場合、通常は動的コンパイラ、プロセッサ、およびメモリサブシステムを指します)は、スレッド単体での実行結果が、プログラム内でステートメントが発生した順序(プログラム順序とも呼ばれます)ですべてのステートメントが実行された場合とまったく同じであることが保証される限り、有用な実行最適化を自由に導入できます。[ 5 ]
この主な注意点は、as-if-serialセマンティクスが、異なるスレッドがデータの異なるビューを持つことを妨げないということです。メモリ モデルは、データの読み取り時に返される値について明確なガイダンスを提供します。基本ルールは、スレッドの as-if-serialセマンティクスに違反しない限り、個々のアクションの順序を変更できることを意味し、ロックの取得や解放など、スレッド間の通信を伴うアクションは、その前に発生したアクションが、その影響を見る他のスレッドから見えることを保証します。たとえば、ロックの解放前に発生したすべてのことは、同じロックのその後の取得後に発生したすべてのことよりも前に順序付けられ、見えるようになります。[ 6 ]
数学的には、プログラムによって実行されるすべてのアクションに対して、発生順序と呼ばれる部分順序が存在します。発生順序はプログラム順序を包含します。プログラム順序で一方のアクションが他方のアクションより先に発生する場合、発生順序でもそのアクションは他方のアクションより先に発生します。さらに、ロックの解放とその後の取得は、発生順序グラフのエッジを形成します。読み取りは、書き込みが発生順序における何らかのパスに沿って、その読み取りより前にその変数に対して行われた最後の書き込みである場合、または書き込みが発生順序においてその読み取りに対して順序付けられていない場合に、書き込みの値を返すことが許可されます。
Javaのメモリモデルは、普及しているプログラミング言語に包括的なメモリモデルを提供する最初の試みでした。[ 7 ] これは、並行システムや並列システムの普及が進み、そのようなシステムに明確な意味論を持つツールやテクノロジーを提供する必要性が高まったことによって正当化されました。それ以来、メモリモデルの必要性はより広く受け入れられるようになり、C++などの言語にも同様の意味論が提供されています。[ 8 ]
Java メモリ モデルは、マルチスレッド コードでどのような動作が許容されるか、またスレッドがメモリを介してどのように相互作用するかを記述します。プログラム内の変数と、実際のコンピュータ システムでメモリまたはレジスタに変数を格納および取得する低レベルの詳細との関係を記述します。これは、さまざまなハードウェアとさまざまなコンパイラの最適化を使用して正しく実装できる方法で行われます。