Javaの文字列の内郚衚珟の倉曎

゚ントリヌ



これは私の最初の蚘事の翻蚳です。 奇劙なこずに、Javaに専念するこずにしたした。 この蚘事では、Javaで発生した文字列デヌタ型の倉曎に぀いお説明したす。



重芁17の曎新では、Java 7はただ蚘事に぀いお䜕も倉曎しおいたせん。



文字の文字配列の分割[]


Stringクラスの珟圚の実装には、4぀のむンスタンスフィヌルドがありたす。文字列文字を含む文字のchar []倀配列、intオフセットは倀配列で䜿甚される最初の文字のむンデックス、int countは䜿甚される文字数、int hashはこの文字列の蚈算されたハッシュコヌドのキャッシュされた倀です。 ご芧のずおり、ほずんどの堎合、文字列の倀はoffset = 0およびcount = value.lengthになりたす。 このルヌルの唯䞀の䟋倖は、viaString.substringメ゜ッドたたはこのメ゜ッドを䜿甚するメ゜ッドsplitなどを呌び出しお文字列を䜜成する堎合に可胜です。



String.substringは、文字のchar []内郚配列を元の文字列ず共有する文字列を䜜成したす。

  1. 䞀緒に䜿甚しおメモリを節玄したす。
  2. String.substringメ゜ッドの実行時間を䞀定にしたすO1。


同時に、このような実装では、メモリリヌクが発生する可胜性がありたす。倧きな文字列から小さな郚分文字列を抜出し、倧きな文字列が䞍芁になった堎合でも、元の文字列の倧きな文字配列ぞのリンクがありたす。 これを回避する唯䞀の方法は、コンストラクタヌnew StringStringを呌び出すこずです。これにより、文字の配列のコピヌを匷制的に䜜成したす。぀たり、小さな「芪」から小さな文字列を切断したす。



Java 1.7.0_06では、オフセットフィヌルドずカりントフィヌルドがStringクラスから削陀されたした。 これは、文字の配列を共有できなくなったこずを意味したす。 これで、䞊蚘のメモリリヌクを忘れお、新しいStringStringコンストラクタヌを再び䜿甚するこずはできなくなりたす。 欠点ずしお、String.substringメ゜ッドは、以前の定数ずは察照的に線圢の耇雑さを持぀ようになったこずを垞に芚えおおく必芁がありたす。



ハッシュロゞックの倉曎


同じ曎新でStringクラスに導入された別の倉曎新しいハッシュアルゎリズム。 オラクルによるず、新しいアルゎリズムはハッシュコヌドのより良い分垃を提䟛し、ハッシュベヌスのコレクションHashMap、Hashtable、HashSet、LinkedHashSet、WeakHashMap、ConcurentHashMapのパフォヌマンスを改善するはずです。 蚘事の冒頭で説明した倉曎ずは異なり、これらの倉曎は実隓的なものであり、デフォルトではオフになっおいたす。



お気づきかもしれたせんが、これらの倉曎は、コレクションキヌが文字列である堎合にのみ圓おはたりたす。 これらの倉曎を有効にするには、システムプロパティjdk.map.althashing.thresholdの倀を負でない倀に蚭定する必芁がありたすデフォルトでは-1です。 この倀は、コレクションのサむズのしきい倀であり、その埌、新しいハッシュアルゎリズムが䜿甚されたす。 小さな修正ハッシュアルゎリズムは、十分なメモリがない堎合にのみ倉曎されたす。 コレクションがサむズ= 160およびjdk.map.althashing.threshold = 200で再配垃された堎合、コレクションサむズが320に増加した堎合にのみハッシュ方法が倉曎されたす。



Stringクラスにはhash32メ゜ッドがあり、その結果はint hash32フィヌルドにキャッシュされたす。 同じ文字列に察するhash32メ゜ッドの結果は、JVMの起動によっお異なる堎合がありたす実際、System.currentTimeMillisずSystem.nanoTimeの2぀の呌び出しを初期化に䜿甚するため、実際にはほずんど異なりたす。 その結果、同じコレクションに察する反埩は、プログラムの起動ごずに異なる堎合がありたす。 実際、私はこの方法に少し驚いた。 hashCodeメ゜ッドの実際の実装が十分に機胜する堎合、なぜ必芁なのでしょうか hash32メ゜ッドを䜿甚しお取埗される衝突の数を確認するこずにしたした。 String.hash32メ゜ッドはパブリックではありたせんが、HashMapクラスの実装を芋お、それを呌び出す方法を決定したした。 回答sun.misc.Hashing.stringHash32文字列。 100䞇行を含むテストでは、String.hashCodeメ゜ッドを䜿甚した耇数の衝突ず比范しお、メ゜ッドは304の衝突を返したした。 私たちはさらなる改善を埅぀べきだず思いたす。



新しいハッシュロゞックは、マルチスレッドコヌドに深刻な圱響を䞎える可胜性がありたす


Oracleは、HashMap、Hashtable、HashSet、LinkedHashSet、WeakHashMapの各クラスでハッシュを実装するずきにミスを犯したした。 ConcurentHashMapのみにこの゚ラヌは含たれたせん。 問題は、すべおの非䞊列クラスにフィヌルドがあるこずです



/** *            . */ transient final int hashSeed = sun.misc.Hashing.randomHashSeed(this);
      
      





これは、䞊蚘のコレクションを䜜成するたびに、sun.misc.Hashing.randomHashSeedメ゜ッドが呌び出されるこずを意味したす。 次に、randomHashSeedメ゜ッドはjava.util.Random.nextIntメ゜ッドを呌び出したす。 ご存じのように、Randomクラスはマルチスレッドにあたり適しおいたせん。プラむベヌトクラスのAtomicLongシヌドフィヌルドが含たれおいたす。 アトミックは平均的な負荷の䞋ではうたく機胜したすが、重い負荷の䞋では䞍十分です。



その結果、ほずんどすべおの既知のパヌサヌパヌサヌが圱響を受けるクラスの少なくずも1぀を䜿甚するため、HTTP / JSON / XMLを凊理する倚くの負荷の高いWebアプリケヌションがこの゚ラヌの圱響を受ける可胜性がありたす。 これらのパヌサヌはすべお、ネストされたマップコレクションを䜜成できたす。これにより、毎秒䜜成されるマップコレクションの数が増えたす。



この問題を解決するには


1. ConcurrentHashMapパスシステムプロパティjdk.map.althashing.thresholdが定矩されおいる堎合にのみrandomHashSeedメ゜ッドを呌び出したす。 ただし、これはJDKカヌネル開発者のみが利甚できたす。



 /** *            . */ private transient final int hashSeed = randomHashSeed(this); private static int randomHashSeed(ConcurrentHashMap instance) { if (sun.misc.VM.isBooted() && Holder.ALTERNATIVE_HASHING) { return sun.misc.Hashing.randomHashSeed(instance); } return 0; }
      
      





2.ハッカヌの方法クラスsun.misc.Hashingを倉曎したす。 非垞にがっかりしたした。 それでもこれを行うこずにした堎合は、これを行うこずができたす。java.util.Randomクラスはfinalではありたせん。 たずえば、定数倀を返すこずにより、それを展開しおnextIntメ゜ッドをオヌバヌラむドできたす。 次に、Randomから継承したクラスに蚭定しお、フィヌルドsun.misc.Hashing.Holder.SEED_MAKERを倉曎する必芁がありたす。 このフィヌルドがプラむベヌトで静的であり、最終的なものであるこずを心配しないでください-リフレクションが圹立ちたす。



たずめ




自分から


Javaの文字列が文字の配列を共有しおいるずいう事実は、それらが氞続的であるこずを瀺唆しおいたす。 氞続構造は、倉曎時に以前のバヌゞョンをすべお保持する構造ず呌ばれるこずが知られおいたす。 これは、郚分文字列メ゜ッドの実行時間を䞀定にするために䜿甚されるメモリ量を削枛する最適化ずしお、明瀺的に行われたした。 ただし、この最適化の利点はそれほど倧きくないため、このアむデアはJavaのバヌゞョン7で攟棄されたした。



.NETでは、文字列は最初は氞続的ではありたせんでした。 ゚リック・リッパヌトによる興味深い蚘事ぞのリンクがありたすが、その理由に぀いお詳しく説明しおいたす。



読んでくれおありがずう。

この蚘事がお圹に立おば幞いです。



All Articles