あなたが同期を知っている必要があります(中編:ロックのアップグレード)

第I部では、私たちはここの同期の原理を利用して実装の基本的な違いを話した同期にJDK1.6、JVMのキーワードは非常に複雑な最適化を行って後、これは、アップグレードプロセスの同期ロックの話であり続け、もちろん、目的は、同期を強化することです演奏

本篇测试环境:
JDK版本 :java version "1.8.0_221"
JDK模式 :Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
操作系统:Windows 10 企业版  x64 笔记本
内存容量:8G DDR3
CPU型号 :Intel i7-5500U 2.4GHz
JVM参数 :-Xmx512m -Xms512m -XX:+UseParallelGC复制代码

オブジェクトヘッダ

同期のアップグレードプロセスの話をする前に、まず私たちはものを理解する必要があり、 オブジェクトヘッダ 各オブジェクト内のJVMの実装では、ターゲットの頭を持っているだろう、それはオブジェクトの情報を格納するためのシステムであり、オブジェクトヘッダは、関係者は語っています一部の値下げとして、彼は様々な実装をロックするための鍵です。32ビットのデータが値引きされ、64ビットシステムでは64ビットのデータである32ビットシステムでは、彼は、ハッシュオブジェクトの値、対象の年齢、ロックポインタ情報等が格納されています。要するに、ロックオブジェクトが占有されている、それはでマークダウンのレコードのロックの種類を占めていました。

バイアスされたロック

バイアスされたロックコアアイデア

スレッド競合状態がシステムに存在する場合、スレッドロック同期を保持するキャンセルされているであろう。スレッドT1が存在する場合、要求は場合T1再びそれを保持する場合要するに、それは最初、バイアスされたロック・モードに入った後に、ロックを取得しますロックは、このように性能を向上するように、ロックのアプリケーションの動作を保存、ロックを取得するために進む前に、必要とされない場合。ロックを取得するために他のスレッドがある場合、ロック時の賛成であり、その後T1出口はモードロック付勢。

ロックの偏った表現

ロックオブジェクトがロックモードにバイアスされたとき、オブジェクトヘッダは、情報を保存します。

[保留スレッドをロック付勢|バイアスロックを示す、1に固定され| |対象年齢|タイムスタンプのロック付勢2 01へ最後には、ロック解除バイアス/表します]
【THREAD_ID |エポック|年齢| 1 | 01] t1が再びロックを取得しようとすると、上記の情報をJVMに直接、現在のスレッドがロックバイアスを保持しているかどうかを決定することができます。

バイアスされたロック性能試験

だから、JVMのパフォーマンスの最適化の結果が行う最終的にロックする傾向がありますか?次に、我々はテストコードの実用的な効果を見に来て

private static List<Integer> list = new Vector<>();
public static void main(String[] args) {
    long start = System.currentTimeMillis();
    for (int i = 0; i < 10000000; i++) {
        list.add(i + 2);
    }
    long end = System.currentTimeMillis();
    System.out.println(end - start);
}复制代码

:結果の精度をテストするために、我々は、2つの追加のパラメータを設定する必要がある -XX:+UseBiasedLocking表現がオープンにロックセットをバイアス-XX:BiasedLockingStartupDelay=0表現起動した後にバイアスされ、ロックトップを開始するために4秒デフォルトJVMコード、設定されていない場合、JVMは、始動直後始まりロックバイアスを我々は、ベクトルが書かれている使用して、初期化する準備を、よく知られた内部アクセス動作が同期されるベクトルロック同期を制御するために使用され、それぞれの時間は、addメソッドは、ロック・リスト・オブジェクトを要求し、その後、私は行で10回実行します最後に、出力値が約440から470、閉鎖バイアスロッキング(-XX:-UseBiasedLocking)一方おそらく同じコードはまた、約670から690の最後の出力値、10回実行した後に、他の要因を取り除く估算性能差距在百分之20周り。

ロックバイアスの問題

資源のない状態でロックする傾向があり、オーバーヘッドロックによる性能を低減することが可能な限り競争をマルチスレッド化されているが、問題を無視していない、とき私たちのアプリケーション内のスレッドの競争が激しく時に多数のスレッドであり、その我々は、システムのスレッド内で熾烈な競争を比較すれば、そう、パフォーマンスが向上しますだけでなく、システムのパフォーマンスが低下する可能性がロックを要求し続け、だけでなく、偏ったロックを使用して、ロックモードを維持するのに有利に維持することは困難でロックし、この時間をリードします、直接バイアスされたロッキングとしてシャットダウン:-XX:-UseBiasedLocking

軽量ロック

(ロックを付勢するとき、他のスレッドの競合)をバイアスロックが失敗した場合、JVMはすぐにスレッドを一時停止するが、ロックが発生する元に戻す傾向がありません、そのオブジェクトは、2つの状態であってもよく、ものです:軽量ロックの保有者であればスレッドは、その後、時間値下げオブジェクトが、このとき、状態(軽量)1がロックバイアスされていない、ロックフリー状態にしないでください[prt | 00] locked、それは2の後に00 、それは必要がオブジェクトヘッダ必要にのみチェックするとき、スレッドは、オブジェクトの軽量ロックを保持しているかどうかを決定するときに、ロックの内部にスタック領域を保持しているスレッドへのポインタとして単に被検者の頭であります現在のスレッドのアドレス範囲にスタックポインタにするかどうか。実際JVM内で使用される軽量なロックが達成するBasicObjectLockオブジェクトであり、BasicLock内部オブジェクトは、JVMの実装をロックを保持しているオブジェクトへのポインタを含む、BasicLock値下げまず、元のオブジェクトをコピーしますその後、値下げにアドレスをコピーするために、原子CAS操作を使用すると、コピーが成功した場合、BasicLockヘッドオブジェクト成功したロックを表し、それ以外の場合は失敗をロックし、それが失敗した場合、その後、軽量ロックロックインフレがあるかもしれません!

スピンロック

ロック拡張を行った後、この時間は、スレッドの可能性が高い動作を停止するオペレーティング・システム・レベルで直接行われる、カーネルモードへのユーザーモードの切り替え処理を意味して発生し、パフォーマンスの損失は、この時間が比較的大きいです!だから、ロック膨張後、JVMは中断されたスレッドを回避するために、最終的な努力、スピンロックを利用します。

ここでは、スピンの平均は、現在のスレッドがロックを取得していない中断されることはありませんが、空ループ(スピン)を実行する場合、取得すれば、Nサイクルの後に、現在のスレッドが、再びロックを要求するということですまだ取得できない場合は成功し、通常の実行は、中断されます。

スピンロック問題

スピンロックは異なるシナリオの下で異なるパフォーマンス消費を持って、例えば、競争が占領パリティ短いシーンの下のロック時間、ロックのために非常に強いではない、スピンロックが効果的にオペレーティングシステムを回避することができ、保留中の数をロックし、それが減少することですスイッチング時間カーネルモードへのユーザの状態、しかし一方、以下、シーンのスレッド数ロックより激しい競争、またはロックが長い時間を占めている場合は低く、さらに、スレッドがロックを取得し、他のN個のスレッドは、ロックを競合しなければならこれは、スレッドの多くが回転されますが、ロックを取得することはできません一定時間後に、オペレーティングシステムがまだ保留中であることを意味しますが、より多くの時間とCPUリソースの無駄(スレッド数空のスピン)であります

スピンロックの設定

JDK6では、JVMは、スピンロックを有効にするためのパラメータを提供することである:-XX:+UseSpinningそれは一緒に使用することができる:-XX:PreBlockSpinスピン-スピンロックの数を設定するためのパラメータ(デフォルトスピン10回)

しかし、JDK7上記のバージョンでは、JVMは、これら2つのパラメータを放棄され、JVMのスピンロックはデフォルトで有効になっており、自動的にスピンの数を調整します

ロック排除

ロックのキャンセルは、より直接的なロック最適化技術である時にJVM JITコンパイラによって生成された最適化への道である、JVMは、プログラムのスキャン操作のコンテキストをやって、ロックを外し、直接共有リソースを競うことができない、缶不要なロック操作を保存

たとえば、次のコード:

private static String t(String s1, String s2){
    StringBuffer buffer = new StringBuffer();
    buffer.append(s1);
    buffer.append(s2);
    return buffer.toString();
}复制代码

私たちは、StringBufferのStringBuilderのバージョンは、スレッドセーフですが、上記のコードでは、変数のスコープは、内部バッファメソッド本体に限定されているロックされたと言うことができるすべて知っているが、そこにメソッドへの脱出になることはできませんが、明らかに使用するスレッドセーフにする必要はありませんロックは、そのようなコードを排除するために最適化することができるように、文字列の連結を行う方法

以下の起動パラメータを追加します。

-server					// 锁消除必须在Server模式下
-Xcomp					// 使用编译模式
-XX:+DoEscapeAnalysis 			// 打开逃逸分析
-XX:+EliminateLocks 			// 打开锁消除
-XX:BiasedLockingStartupDelay=0         // JVM启动立刻打开偏向锁
-XX:+UseBiasedLocking			// 打开偏向锁复制代码

ロックに関連付けられている構成は、可変バッファは逃げ場方法tのではありません、おそらく変数は、上記の例では、スコープから脱出かどうかを確認するために言うことを意味し、私は簡単にエスケープ分析を説明エスケープ分析を、排除することですまた、エスケープとして知られているスコープ機能は、この場合には、実現しなかった、逆に、変数の除去の最適化をロックするために、JVMの内部バッファトン以下のメソッドの場合:

private static StringBuffer t(String s1, String s2){
    StringBuffer buffer = new StringBuffer();
    buffer.append(s1);
    buffer.append(s2);
    return buffer;
}复制代码

上記可変バッファの方法は、外部メソッドに、内部からTエスケープをエスケープ発生、JVMは、可変バッファロック操作を排除することはできません

ロックを開いた後、理論的にパフォーマンスが向上しているはず排除するが、私はより多くの実際のテスト構成よりも使い、開閉ロックの除去が実際に有意差の結果であるので、興味のある友人ができた場合の試験結果は、ここに掲載されていませんセルフテスト、または友人が議論するメッセージを投稿することができます知っている理由があります



おすすめ

転載: juejin.im/post/5defb228518825124c50d583