同時実行性は、複数の操作を同時に実行できる堅牢でスケーラブルなアプリケーションの開発にとって非常に重要です。ただし、同期に関しては代償を支払う必要があります。ロックの取得と解放に伴うオーバーヘッドにより、パフォーマンス コストが発生します。これらのパフォーマンス コストを軽減するために、バイアスされたロック、ロックの削除、ロックの粗密化、軽量ロックと重量ロックの概念など、いくつかの最適化が JVM に組み込まれています。
この記事では、これらの最適化について詳しく説明し、マルチスレッド Java アプリケーションの同期がどのように改善されるかを説明します。
Java ロックの基本
Java では、ブロックまたはメソッドの同期により、コードの重要なセクションを一度に 1 つのスレッドだけが実行できるようになります。これは、マルチスレッド環境内でのリソースの共有を考慮する場合に特に重要です。 Java は、組み込みロックに依存してこれを実装します。または、同期ブロックを使用してスレッドへのアクセスを管理するのに役立つ、オブジェクトまたはクラスに関連付けられたモニターと呼ばれることもあります。
同期はスレッドの安全性のために必要ですが、競合が少ない場合、または競合がまったくない場合には、非常にコストがかかる可能性があります。ここで、JVM 最適化がフレームワークに組み込まれます。したがって、ロックのコストが削減され、全体的なパフォーマンスが向上します。
1.バイアスロック
バイアスロックとは何ですか?
バイアス ロックは、ロック取得のオーバーヘッドの削減を目的とした最適化です。これは、単一のスレッドによって支配されているか、単一のスレッドによってほとんどアクセスされるロック取得のコストを削減するために最適化されています。このようなプログラムは、多くの場合、他のスレッドと競合することなく、同じスレッドによってロックを取得および解放します。 JVM はこのパターンを認識し、ロックをその特定のスレッドにバイアスします。次のロックの取得はほぼ無料で行われます。
バイアスされたロックはどのように機能しますか?
バイアスされたロックが有効になっている場合、スレッドが初めてロックを取得したときに、そのロックはそのスレッドに対してバイアスされます。スレッドの ID はロック オブジェクトのヘッダーに記録され、そのスレッドによるその後のロックの取得には同期はまったく含まれません。ロックが現在のスレッドに偏っているかどうかをチェックするだけで、これは非常に高速で非ブロッキングな操作です。 .
別のスレッドがロックを取得しようとすると、バイアスはキャンセルされ、JVM は標準の不バイアス ロック メカニズムに戻ります。この段階では、これは標準ロックになっており、2 番目のスレッドは標準ロック プロセスを通じてロックを取得する必要があります。
バイアスロックの利点
パフォーマンス: 偏ったロックでの同じスレッドの取得は、ほぼ無料のロック取得です。
したがって、他のスレッドがロックの取得に関与する可能性がないため、競合処理は必要ありません。
オーバーヘッドの低減: 競合が発生した場合を除き、ロックの状態を変更したり、同期関連のメタデータを変更したりする必要はありません。
バイアス ロックはいつ使用されますか?
バイアス ロックは、シングルスレッド アプリケーションやマルチスレッドでロックの競合が少ないアプリケーションなど、ロックが主に同じスレッドによってアクセスされるアプリケーションで役立ちます。ほとんどの JVM ではデフォルトで有効になっています。
バイアスされたロックを無効にする方法
バイアス ロックはデフォルトで有効になっていますが、以下のように JVM フラグを使用して無効にすることもできます。
-XX:-UseBiasedLocking
2.ロック解除
ロック解除とは何ですか?
ロックの削除は、JVM が一部の不要な同期 (ロック) を完全に削除する非常に強力な最適化です。 JIT コンパイル中に、同期が必要ではないことが判明する機会がないかコードを検査します。これは通常、ロックが 1 つのスレッドのみによってアクセスされた場合、または JVM が同期に使用するオブジェクトが異なるスレッド内で同じオブジェクトを共有していない場合に発生します。 JVM はロックが不要になったと判断すると、ロックを削除します。
ロックの削除はどのように機能しますか?
JIT コンパイルのエスケープ分析フェーズでは、JVM はオブジェクトが単一のスレッドに限定されているか、ローカル コンテキストでのみ使用されているかをチェックします。オブジェクトがそれを作成したスレッドのスコープからエスケープしないため、そのオブジェクトの同期を削除できる場合は、そのようになります。
たとえば、オブジェクトが完全にメソッド内で作成され使用される (スレッド間で共有されない) 場合、JVM は他のスレッドがそのオブジェクトにアクセスできないことを認識し、そのためすべての同期が冗長であると認識します。このような場合、JIT コンパイラーはロックを完全に削除します。
ゼロ ロック オーバーヘッド: 不必要な同期を排除すると、そもそも JVM がロックの取得と解放にかかるコストを支払う必要がなくなります。
スループットの向上: デッド同期は、特にコードに多くの同期ブロックが含まれている場合、アプリケーションのスループットの向上につながることがあります。
このコードを見てください:
public void someMethod() { StringBuilder sb = new StringBuilder(); synchronized (sb) { sb.append("Hello"); sb.append("World"); } }
この場合、StringBuilder は someMethod 内でのみ使用され、他のスレッド間では共有されないため、sb での同期は必要ありません。これを確認することで、JVM はエスケープ分析を実行してロックを解除できます。
3.ロック粗大化
ロック粗密化とは何ですか?
ロックの粗密化は、ループまたはコードの小さなセクションでロックを継続的に取得および解放するのではなく、JVM がロックの範囲を拡大してコードのより多くの部分をカバーする最適化です。
ロック粗調整作業
JVM は、タイトなループまたは複数の隣接するコード ブロックが頻繁にロックを取得および解放することを検出した場合、ループの外またはコードの複数のブロックにわたってロックを取得することにより、ロックを粗大化する可能性があります。これにより、ロックレスの取得と解放を繰り返すとコストが高くなり、スレッドがより多くの反復にわたってロックを保持できるようになります。
コード例: ロック粗密化
次のコード スニペットを考えてみましょう:
for (int i = 0; i < 1000; i++) { synchronized (lock) { // Do something } }
ロックの粗密化により、ロックの取得がループの外にプッシュされるため、スレッドはロックを 1 回だけ取得します。
synchronized (lock) { for (int i = 0; i < 1000; i++) { // Do something } }
JVM は、より多くのロックの取得と解放を回避することで、パフォーマンスを大幅に向上させることができます。
ロック粗大化のメリット
ロックのオーバーヘッドの自由度の低下: 粗化により、特に何千回も反復されたループなどのホットスポット コードでのロックの取得と解放が回避されます。
パフォーマンスの向上:
長期間ロックすると、ロックを行わずにそのようなロックが複数回取得および解放されるシナリオと比較して、パフォーマンスが向上します。
4.軽量ロックと重量ロック
JVM は、スレッド間の競合の程度に基づいて 2 つの異なるロック手法を使用します。このような手法には、軽量ロックと重量ロックが含まれます。
軽量ロック
軽量ロックは、競合ロックがない状態で行われます。つまり、1 つのスレッドだけがそのロックを取得しようとしています。このようなシナリオでは、JVM はロックを取得しようとするときに CAS 操作を使用して取得を最適化しますが、これは強力な同期なしで発生する可能性があります。
ヘビーウェイトロック
複数のスレッドが同じロックを取得したい場合。つまり、競合が発生すると、JVM はこれを強力なロックにエスカレーションします。これには、OS レベルでスレッドをブロックし、OS レベルの同期プリミティブを使用してスレッドを管理することが含まれます。ヘビーウェイト ロックは、実際には OS によるコンテキストの切り替えとスレッドの管理を必要とするため、低速になります。
ロックエスカレーション
軽量ロックで競合が発生した場合、JVM は競合を重量ロックにエスカレートする可能性があります。ここでのエスカレーションとは、高速なユーザーレベルのロックから、スレッドのブロックを含む、より高価な OS レベルのロックに切り替えることを意味します。
軽量ロックの利点
ロックの迅速な取得: 競合がない場合、軽量ロックは OS レベルの同期を回避するため、重量ロックよりもはるかに高速です。
ブロッキングの減少: 競合がなければ、スレッドはブロックされず、遅延が少なくなり、直線的に増加します。
重量ロックの欠点
パフォーマンスのオーバーヘッド: 重量ロックでは、スレッドのブロック、コンテキストの切り替え、スレッドのウェイクアップといったコストが発生し、非常に高い競合状況ではパフォーマンスが低下します。
これらすべての最適化は、JVM がマルチスレッド アプリケーションのパフォーマンスを向上させるのに役立ち、開発者は同期オーバーヘッドをあまり犠牲にすることなく、安全な同時コードを作成できるようになります。これらの最適化を理解すると、開発者がより効率的なシステムを設計するのに役立ちます。特に、ロックによってパフォーマンスが低下する場合に役立ちます。
以上がJVM ロックの最適化についての詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。