序文
これはこの期間中に非常にアイドル状態であるため、JDKソースコードを見ました。一般的なシニア開発エンジニアは、ソースコードを読むことで自分自身を改善できます。この記事では、JDKソースコードにいくつかの「小さなヒント」をまとめて、参照と学習のためにそれらを共有しています。以下ではあまり言いません。詳細な紹介を一緒に見てみましょう。
1 i ++対i--
String Sourceコードの行985は、等しい方法です
while(n - != 0){if(v1 [i]!= v2 [i])false; i ++; }このコードは、文字列が等しいかどうかを判断するために使用されますが、判断を下すためにi - != 0を使用する奇妙なものがあります。私たちは通常I ++を使用しませんか?なぜ私を使用するのですか?そして、サイクルの数は同じです。その理由は、コンピレーション後にもう1つの指示があるからです。
I--操作自体はCPSRに影響します(現在のプログラムステータスレジスタ)。 CPSRの一般的なフラグはN(結果は負)、Z(結果は0)、C(キャリー)、およびO(オーバーフロー)です。 i> 0は、Zフラグで直接判断できます。
I ++操作はCPSR(現在のプログラムステータスレジスタ)にも影響しますが、O(オーバーフロー付き)フラグのみに影響します。これは、i <nの判断に役立ちません。したがって、追加の比較命令が必要です。つまり、ループごとにもう1つの命令を実行する必要があります。
簡単に言えば、0と比較して、1つの命令が1つ少なくなります。したがって、I-、ハイエンド、雰囲気、ハイエンドをリサイクルします。
2メンバー変数とローカル変数
JDKソースコードは、ローカル変数をほぼ使用して、あらゆる方法でメンバー変数を受け入れます。たとえば
public int compareto(string anothestring){int len1 = value.length; int len2 = anothestring.value.length;ローカル変数はメソッドのスレッドスタックで初期化され、メンバー変数はヒープメモリで初期化されるため、明らかに前者はより高速であるため、メソッドで直接メンバー変数を使用しないようにしますが、代わりにローカル変数を使用します。
3意図的にレジスタにロードする&&ロックの外側に時間のかかる操作を置く
concurrenthashmapでは、ロックセグメントの動作は非常に興味深いです。直接ロックではありませんが、スピンロックに似ています。それは繰り返しロックを取得しようとします。ロックを取得する過程で、リンクリストを通過するため、最初にデータがレジスタキャッシュにロードされ、ロックプロセスの利便性が回避されます。同時に、新しいオブジェクトを生成する操作もロックの外側に配置され、ロックでの時間のかかる操作を避けます
final v put(k key、int hash、v value、booleanのみ){ /**このセグメントに書き込む前に、最初にセグメントの排他的ロックを取得する必要があります。 lock()を強制するのではなく、*/ hashentry <k、v> node = trylock()を試すことです。 null:scanandlockforput(key、hash、value); scanandlockforput()ソースコード
private hashentry <k、v> scanandlockforput(k key、int hash、v value){hashentry <k、v> first = entryforhash(this、hash); Hashentry <k、v> e = first; Hashentry <k、v> node = null; int retries = -1; //ノードを見つけたときに負//ループしてロックを取得するwhile(!tryLock()){hashentry <k、v> f; //以下の最初に再確認する(<0){if(e == null){if(node == null)//投機的にノードを作成する//ハッシュビットには値がない、新しいオブジェクトを作成する必要がなく、新しいnode = new hashentry <k、v>(hash、key、key、key、key、key、key、key、key、key)を作成するためのput()メソッドに移動する必要がない。 retries = 0; } //ハッシュ位置キーは同じであり、スピンロックに退化します。 else // retriesは、リンクされたリストをキャッシュe = e.nextに自動的に読み取ることができます。 } //> 0を再試行すると、スピンロックになります。もちろん、レトリの数がMAX_SCAN_RETRIES(シングルコア1マルチコア64)を超える場合、それをスナッチしないでください。ブロッキングキューを入力してロック//ロック()はロックを取得した後に戻るまでブロック方法です。壊す; } else if((retries&1)== 0 && //現時点では大きな問題があります。つまり、新しい要素がリンクリストに入り、新しいヘッダー//になります。 //エントリが変更された場合の再交換retries = -1; }} return node;} 4最初にそれを使用して、オブジェクトのequality ==を決定できます
オブジェクトが等しいかどうかを判断する場合、==最初に使用できます。==は非常に高速なアドレスを直接比較しますが、等しいものは比較的遅いほとんどのオブジェクト値を比較します。可能であれば、a == b ||を使用できますA. equals(b)オブジェクトが等しいかどうかを比較します。
5過渡について
一時的なものはシリアル化を防ぐために使用されますが、ハッシュマップソースコードの内部配列は一過性として定義されます。
/***必要に応じてサイズ変更されたテーブル。長さは常に2つの力でなければなりません。 */ TRANIENT ENTRY <K、V> [] Table =(Entry <K、V> [])empty_table;
それでは、内部のキー価値のペアをシリアル化できませんか?ネットワーク上のハッシュマップを使用して送信することは不可能ではありませんか?実際、そうではありません。
効果的なJava 2nd、item75、Joshuaは言及した:
たとえば、ハッシュテーブルの場合を検討してください。物理的
表現は、キー価値を含むハッシュバケットのシーケンスです
エントリ。エントリが存在するバケツはハッシュの関数です
そのキーのコードは、一般的には同じであることが保証されていません
JVM実装からJVM実装まで。実際、そうではありません
実行から実行まで同じであることが保証されています。したがって、を受け入れます
ハッシュテーブルのデフォルトのシリアル化されたフォームは、深刻なものになります
バグ。ハッシュテーブルをシリアル化して脱必要にすると、
不変剤がひどく腐敗していたオブジェクト。
理解する方法は? Hashmap.get()/put()を見て、読み取りマップがObject.hashcode()に基づいていることを知り、読み取り/書き込みのバケットを決定します。 object.hashcode()はネイティブメソッドであり、JVMが異なる場合があります。
たとえば、エントリをHashMapに保存すると、キーは文字列「文字列」です。最初のJavaプログラムでは、「文字列」のハッシュコード()は1で、バケット番号1が保存されます。 2番目のJavaプログラムでは、「文字列」のハッシュコード()が2であり、バケット番号2が保存される場合があります。デフォルトのシリアル化が使用されている場合(エントリ[]テーブルは過渡を必要としません)、このHashMapは最初のJavaプログラムからのシリアル化を通じて2番目のJavaプログラムをインポートすると、そのメモリ分布は同じであり、これは間違っています。
たとえば、Hashmapにキー値ペアのエントリを保存すると、key = "Fang Laosi"、最初のJavaプログラムでは、「Fang Laosi」のハッシュコード()が1で、テーブル[1]が保存されます。 OK、今では別のJVMプログラムに渡されます。「Fang Laosi」のHashCode()は2である可能性があるため、テーブル[2]に移動して取得します。結果は存在しません。
Hashmapの現在のreadObjectおよびwriteObjectは、コンテンツを出力/入力し、ハッシュマップを再生するために使用されます。
6 charを使用しないでください
CharはJavaのUTF-16でエンコードされており、2バイトで、2バイトはすべての文字を表すことはできません。 2バイトはBMPと呼ばれ、もう1つは高い代理と低い代理と呼ばれ、スクリビングによって表される4バイトの文字を形成します。たとえば、文字列ソースコードのインデックス:
//ここに、範囲のpublic int indexof(int ch、int fromindex)の判断を促進するためにcharを受け入れるINTがあります{final int max = value.length; if(fromindex <0){fromindex = 0; } else if(fromindex> = max){//注:fromindexは-1 >>> 1に近い場合があります。 return -1; } // bmp範囲のif(ch <character.min_supplementary_code_point){//ここでほとんどの場合(ChはBMPコードポイントまたはA //ネガティブ値(無効なコードポイント))final char [] value = this.value; for(int i = fromindex; i <max; i ++){if(value [i] == ch){return i; }} return -1; } else {//それ以外の場合、4バイト判断方法に移動します。 }}したがって、JavaのCharは、UTF16のBMP部分文字のみを表すことができます。 CJK(中国、日本、韓国統一された表意文字)の部分的な拡張キャラクターセットの場合、それらを表現することはできません。
たとえば、下の図のext-aパーツを除いて、charを表すことはできません。
さらに、charを使用する必要があるという別のことわざがあります。パスワードに文字列を使用しないでください。文字列は定数(つまり、作成後に変更することはできません)であり、一定のプールで保存されます。他のプロセスがプロセスのメモリをダンプできる場合、定数プールがダンプされるとパスワードが漏れ、char []は他の情報を変更することができます。つまり、パスワードの漏れのリスクを軽減します。
しかし、私はあなたがメモリを捨てることができると個人的に思います。 charはそれを防ぐことができますか?文字列が一定のプールでリサイクルされず、他のスレッドで一定のプールから直接読み取られない限り、おそらく非常にまれです。
要約します
上記は、この記事のコンテンツ全体です。この記事の内容には、すべての人の研究や仕事に特定の参照値があることを願っています。ご質問がある場合は、メッセージを残してコミュニケーションをとることができます。 wulin.comへのご支援ありがとうございます。