String の 2 つのインスタンスがあり、それらが等しい場合、Java では同じメモリを共有します。これは内部でどのように実装されていますか?
編集: 私のアプリケーションは多数の String オブジェクトを使用していますが、その多くは同一です。カスタムフライウェイト実装の作成を避けるために、Java String 定数プールを利用する最良の方法は何ですか?
String の 2 つのインスタンスがあり、それらが等しい場合、Java では同じメモリを共有します。これは内部でどのように実装されていますか?
編集: 私のアプリケーションは多数の String オブジェクトを使用していますが、その多くは同一です。カスタムフライウェイト実装の作成を避けるために、Java String 定数プールを利用する最良の方法は何ですか?
String の 2 つのインスタンスがあり、それらが等しい場合、Java では同じメモリを共有します。
これは実際には 100% 真実ではありません。
このブログ投稿は、その理由と、文字列定数プールとは何かについての適切な説明です。
のソース コードを見てくださいjava.lang.String
(Java API 全体のソースは JDK の一部です)。
要約すると、 String は a のサブシーケンスをラップしchar[]
ます。そのバッキングchar[]
は決して変更されません。これは、これをクラスchar[]
外に漏らしたりキャプチャしたりしないことによって達成されます。String
ただし、複数Strings
で同じものを共有できますchar[]
( の実装を参照String.substring
)。
他の回答で説明されているように、インターンのメカニズムもあります。
文字列リテラルは Java にインターンされているため、複数の参照を持つ String オブジェクトは実際には 1 つだけです (それらが等しい場合、常にそうとは限りません)。詳細については、java.net の記事All about intern()を参照してください。
また、セクション3.10.5 String Literals of the JLS には、文字列がいつインターンされ、いつ区別されるかについて説明する良い例/説明があります。
それは必ずしも真実ではありません。例:
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true
しかし:
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false
現在、2 番目の形式は推奨されていません。一部の人 (私を含む)String
は、パブリック コンストラクタさえも持つべきではないと考えています。上記のより良いバージョンは次のようになります。
String s1 = new String("hello").intern();
String s2 = new String("hello").intern();
System.out.println(s1 == s2); // true
明らかに、定数に対してこれを行う必要はありませんString
。それは説明的です。
これに関する重要な点は、関数から渡された場合、または関数から取得した場合、 canonicalであることにString
依存できないということです。canonicalは次の等式を満たします。String
Object
a.equals(b) == b.equals(a) == (a == b)
与えられた の非null
インスタンスa
の場合。b,
Class
編集した質問に答えるために、Sun JVM には-XX:+StringCache
オプションがあります。これにより、私の観察では、文字列が重いアプリケーションのメモリ フットプリントを大幅に削減できます。
それ以外の場合は、文字列をインターンするオプションがありますが、それについては注意が必要です。非常に大きく、参照されなくなった文字列は、JVM の存続期間中、引き続きメモリを使用します。
編集(コメントに応じて):ここからStringCacheオプションについて最初に知りました:
-XX:+StringCache 共通に割り当てられる文字列のキャッシュを有効にします。
Tom Hawtinは、いくつかのベンチマークを改善するためのある種のキャッシングについて説明しています。私がIDEAに置いたときの私の観察は、メモリフットプリント(完全なガベージコレクションの後)がそれを持っていないことよりもはるかに減少したことでした. これは文書化されたパラメーターではなく、実際には一部のベンチマークの最適化に関するものである可能性があります。私の観察では、それは役に立ちましたが、それに基づいて重要なシステムを構築するつもりはありません。
注意すべき2つのこと:
new String("abc")
、リテラルを使用してください"abc"
。intern()
プールされている文字列を常に返します。
同一の文字列が可能な値の固定セットから取得される場合、ここで必要なのはタイプ セーフな列挙です。文字列の数が減るだけでなく、より堅実なアプリケーションになります。アプリ全体で、この String にセマンティクスが関連付けられていることがわかります。
私のお気に入りの最適化は、単に高速化するだけでなく、コードをより良くするものとして常に擁護できるものです。そして、10 回中 9 回、String を具象型に置き換えると、より正確で自己文書化されたコードになります。