java.lang.String.intern()を使用することは良い習慣ですか?


194

についてのJavadoc String.intern()は詳細を提供していません。(簡単に言うと、文字列の正規表現を返し、インターンされた文字列をを使用して比較できるようにします==

  • この関数をいつ使用するのString.equals()ですか?
  • Javadocに記載されていない副作用、つまりJITコンパイラによる多かれ少なかれ最適化はありますか?
  • のさらなる使用はありString.intern()ますか?

14
intern()の呼び出しには、それ自体のパフォーマンスへの影響があります。パフォーマンスを向上させるためにintern()を使用してテストする必要があります。これにより、プログラムを大幅にスピードアップして、複雑さを大幅に高めることができます。これを使用して、比較的反復的な値を持つ大きなテーブルのメモリ消費を減らすこともできます。ただし、どちらの場合も、他のオプションの方が適している場合があります。
Peter Lawrey、2010

はい、intern()は独自のパフォーマンスに影響を与えます。特に、intern()のコストは、インターン文字列を参照し、それらへの参照を維持するにつれて直線的に増加するためです。少なくとも、sun / oracle 1.6.0_30 vm。
lacroix1547 2012年

回答:


125

String.equals()を優先してこの関数をいつ使用しますか

参照によって文字列を比較できるため、速度が必要な場合(==は等号より高速です)

Javadocに記載されていない副作用はありますか?

主な欠点は、比較する文字列をすべて実際にintern()することを忘れないようにする必要があることです。すべての文字列をintern()することを忘れるのは簡単です。そうすれば、混乱を招くほど不正確な結果を得る可能性があります。また、万が一のために、内部化される文字列に依存していることを非常に明確に文書化してください。

文字列を内部化することを決定した場合の2番目の欠点は、intern()メソッドが比較的高価になることです。一意の文字列のプールを管理する必要があるので、(文字列が既に内部化されている場合でも)かなりの作業が行われます。したがって、コード設計では注意してください。たとえば、入力時に適切なすべての文字列をintern()して、もう心配する必要がないようにしてください。

(JGuruから)

3番目の欠点(Java 7以下のみ):インターンされた文字列は、通常は非常に小さいPermGenスペースに存在します。十分な空きヒープ領域があるOutOfMemoryErrorが発生する可能性があります。

(Michael Borgwardtから)


64
3番目の欠点:インターンされた文字列は、通常は非常に小さいPermGenスペースにあります。十分な空きヒープ領域があるOutOfMemoryErrorが発生する可能性があります。
Michael Borgwardt

15
AFAIKの新しいVMでは、PermGenスペースもガベージコレクションされます。
Daniel Rikowski、2009

31
インターンはメモリ管理についてであり、比較速度ではありません。との違いはif (s1.equals(s2))if (i1 == i2)先頭の文字が同じ長い文字列が多数ない限り最小限です。(URL以外の)ほとんどの実際の使用では、文字列は最初の数文字内で異なります。とにかく、長いif-elseチェーンはコードのにおいです。列挙型とファンクターマップを使用してください。
kdgregory 2010年

25
プログラム全体で引き続きs1.equals構文を使用できます。DONTは==を使用し、.equalsは内部で==を使用して評価を短絡します
gtrak

15
Michael Borgwardtさんは、抑留された文字列はガベージコレクションできないとは言いませんでした。そして、それはFALSEアサーションです。マイケルのコメントが(正しく)言っていることは、それよりも微妙です。
スティーブンC

193

これは(ほとんど)文字列の比較とは関係ありません。文字列インターニングは、アプリケーションに同じ内容の文字列が多数ある場合のメモリ節約を目的としています。String.intern()アプリケーションを使用することにより、長期的には1つのインスタンスのみが存在し、副作用として、通常の文字列比較の代わりに高速参照等価比較を実行できます(ただし、これは通常、インターンだけを忘れることで簡単に破ることができるため、お勧めできません。単一インスタンス)。


4
そうではありません。文字列の内部処理は、各文字列式が評価されるときに常に自動的に行われます。使用される文字の一意の文字列ごとに常に1つのコピーがあり、複数の使用が発生した場合は「内部で共有」されます。String.intern()を呼び出しても、これがすべて行われるわけではありません。内部の正規表現を返すだけです。javadocを参照してください。
Glen Best

16
明確にする必要があります-コンパイル時の定数文字列(リテラルと固定式)のインターニングは常に自動的に行われます。さらに、ランタイムに動的に評価される文字列でString.intern()が呼び出されたときにも発生します。
Glen Best

つまり、ヒープに「Hello」のオブジェクトが1000個あり、そのうちの1つでintern()を実行すると、残りの999個のオブジェクトが自動的に破棄されます。
Arun Raaj 2018年

@ArunRaajいいえ、まだ1000がヒープ上にあり、1つはインターンプールstr.intern()strあり"Hello"ます。これは、後で再利用できるようになります。
Matthieu 2018

37

String.intern()間違いなく最新のJVMで収集されたガベージです。
次のGCアクティビティが原因で、メモリが不足することはありません。

// java -cp . -Xmx128m UserOfIntern

public class UserOfIntern {
    public static void main(String[] args) {
        Random random = new Random();
        System.out.println(random.nextLong());
        while (true) {
            String s = String.valueOf(random.nextLong());
            s = s.intern();
        }
    }
}

GCed以外のString.intern()の神話について(詳細はこちら)を参照してください。


26
OutOfMemoryException-いや、ないコードの上、私の中の脳:javaturning記事を指しているこの記事で、これは... :-)を指しているjavaturning記事へのリンク
user85421

投稿がそのリンクを追加するように編集されたことがわかりますが;)
Riking

3
あなたもあなたがリンクしている外部参照の著者であることに言及したいかもしれません。
–ThorbjørnRavn Andersen 2012

11
Stackoverflowにリンクする外部参照をリンクする@Carlosは、.. Stackoverflowを引き起こす必要があります:)
Seiti

2
@Seiti循環参照は最近簡単に検出されます:p
Ajay

16

私は最近、Java 6、7、8でのString.intern()実装に関する記事を書きました: Java 6、7、8でのString.intern-文字列プーリング

Javaでの文字列プーリングの現在の状況に関する十分な情報が含まれていることを願っています。

一言で言えば:

  • 避ける String.intern()PermGenに入るので、Java 6では
  • 好む String.intern()のJava 7およびJava 8に:それはあなた自身のオブジェクトプールを転がすよりも4-5x少ないメモリを使用しています
  • 必ず調整してください-XX:StringTableSize(デフォルトはおそらく小さすぎます;素数を設定してください)

3
ブログへのリンクを投稿するだけでなく、スパムと見なされる人もいます。さらに、ブログへのリンクは、404の死亡で注目される傾向があります。ここにインラインで記事を要約するか、質問へのコメントにそのリンクを残してください。
マット

3
@ mik1を書いてくれてありがとう!非常に有益で明確で最新の記事。(私はそれへのリンクを自分で投稿するつもりでここに戻ってきました。)
Luke Usherwood 2013年

1
-XX引数について言及していただきありがとうございます。これを使用してテーブル統計を表示することもできます:-XX:+ PrintStringTableStatistics
csadler

13

==を使用した文字列の比較は、equals()を使用した場合よりもはるかに高速です

5時間速くなりますが、文字列比較は通常、アプリケーションの合計実行時間のごく一部にすぎないため、全体的なゲインはそれよりもはるかに小さく、最終的なゲインは数パーセントに希釈されます。

String.intern()は、ヒープから文字列を引き出し、PermGenに配置します

内部化された文字列は、別のストレージ領域に配置されます。永続的な生成は、クラス、メソッド、その他の内部JVMオブジェクトなどの非ユーザーオブジェクト用に予約されているJVMの領域です。この領域のサイズには制限があり、ヒープよりも貴重です。この領域がヒープよりも小さいと、すべてのスペースを使用してOutOfMemoryExceptionが発生する可能性が高くなります。

String.intern()文字列はガベージコレクションされます

JVMの新しいバージョンでは、オブジェクトから参照されていない場合、内部化された文字列もガベージコレクションされます。

上記の3つの点を念頭に置いて、多くの文字列比較を行う場合、String intern()はいくつかの状況でのみ役立つ可能性があることを差し引くことができますが、正確に何を知っているのかわからない場合は、内部文字列を使用しない方がよいでしょう。やっている ...



1
追加するだけで、ヒープメモリの例外は、特にWebアプリケーションなどのスレッドモデルで回復できる場合があります。permgenが使い果たされると、アプリケーションは通常、永続的に機能しなくなり、強制終了されるまでリソースがスラッシュすることがよくあります。
テイラー

7

String.equals()を優先してこの関数をいつ使用しますか

彼らは異なることをするので、おそらく決してしません。

パフォーマンス上の理由から文字列をインターンして、参照が等しいかどうかを比較できるのは、しばらくの間文字列への参照を保持している場合にのみメリットがあります。ユーザー入力またはIOからの文字列はインターンされません。

つまり、アプリケーションでは、外部ソースから入力を受け取り、それをセマンティックな値(識別子など)を持つオブジェクトに処理しますが、そのオブジェクトには、生データと区別できないタイプがあり、プログラマーがどのようにすべきかについて異なるルールがありますこれを使って。

ほとんどの場合、UserIdインターンされ(スレッドセーフな汎用インターンメカニズムを作成するのは簡単です)、open enumのように機能するタイプを作成する方が、java.lang.String型のタイプを作成する方が、たまたまユーザーIDである場合に参照セマンティクスでタイプます。

そうすることで、特定の文字列がインターンされているかどうかを混同することなく、必要な追加の動作をオープンな列挙型でカプセル化できます。


6

利点を認識していません。ある場合は、equals()自体が内部でintern()を使用すると思います(そうではありません)。

インターン()の神話の破壊


7
あなたが利点を認識していないと言っているにもかかわらず、あなたの投稿されたリンクは、==を介した比較を5倍高速であり、したがってテキスト中心のパフォーマンスコードにとって重要であると認識しています
Brian Agnew

3
多くのテキスト比較を行う場合、最終的にはPermGenスペースが不足します。テキストの比較がそれほど多くない場合、速度の違いは重要ではありません。どちらにしても、文字列をインターン()しないでください。それはそれだけの価値はありません。
ボンベ、

また、全体的な相対ゲインは通常は小さくなります。
オブジェクト

そのような論理は有効ではないと思います。良いリンクも!
Daniel Rikowski、2009

1
@DR:どんなロジック?これは大きな誤りです。@objects:申し訳ありませんが、あなたの主張は理由に達していません。を使用するのには非常に適切な理由があり、デフォルトでは使用しないintern非常に適切な理由がequalsあります。あなたが投稿したリンクは完全なブロックです。最後の段落internは、有効な使用シナリオがあることを認めています:重いテキスト処理(例:パーサー)。「あなたが何をしているのかわからない場合、[XYZ]は危険だ」と結論付けるのは非常に卑劣であり、身体的に痛いです。
Konrad Rudolph、

4

ダニエル・ブリュックナーは完全に正しいです。文字列インターニングは、メモリ(ヒープ)を節約するためのものです。私たちのシステムには現在、特定のデータを保持するための巨大なハッシュマップがあります。システムのスケーリングに伴い、ハッシュマップはヒープをメモリ不足にするのに十分な大きさになります(テスト済み)。複製されたすべての文字列をハッシュマップ内のすべてのオブジェクトにインターンすることにより、ヒープ領域を大幅に節約できます。

また、Java 7では、抑留された文字列はPermGenに長くは存在しませんが、代わりにヒープが使用されます。したがって、そのサイズを気にする必要はありません。そうすれば、ガベージコレクションが行われます。

JDK 7では、インターンされた文字列は、Javaヒープの永続的な世代に割り当てられなくなりましたが、代わりに、アプリケーションによって作成された他のオブジェクトとともに、Javaヒープの主要部分(若い世代と古い世代と呼ばれます)に割り当てられます。この変更により、メインのJavaヒープに存在するデータが増え、永続的な世代に含まれるデータが少なくなるため、ヒープサイズを調整する必要がある場合があります。ほとんどのアプリケーションは、この変更によるヒープ使用量の比較的小さな違いしか表示しませんが、多くのクラスをロードしたり、String.intern()メソッドを頻繁に使用する大きなアプリケーションでは、より大きな違いが表示されます。


私はそれを二番目に言わなければなりません:私のソフトウェアでは、ヒープダンプはほとんどのヒープスペースがStringインスタンスによって使用されていることを示しました。それらのコンテンツを見ると、多くの重複があり、に切り替えることintern()にしました。これにより、数百MBを節約できました。
Matthieu

4

Javadocに記載されていない副作用、つまりJITコンパイラによる多かれ少なかれ最適化はありますか?

JITレベルについては知りませんが、専用の文字列を使用して魔法のように効率的に実装されている文字列プールの直接バイトコードサポートがあります。CONSTANT_String_info構造体を使用(より一般的な表現を持つ他のほとんどのオブジェクトとは異なります)。

JVMS

JVMS 7 5.1は言う

文字列リテラルは、クラスStringのインスタンスへの参照であり、クラスまたはインターフェースのバイナリ表現のCONSTANT_String_info構造(§4.4.3)から派生します。CONSTANT_String_info構造体は、文字列リテラルを構成するUnicodeコードポイントのシーケンスを提供します。

Javaプログラミング言語では、同じ文字列リテラル(つまり、同じコードポイントのシーケンスを含むリテラル)がクラスStringの同じインスタンスを参照する必要があります(JLS§3.10.5)。さらに、メソッドString.internが任意の文字列で呼び出された場合、結果は、その文字列がリテラルとして表示された場合に返されるのと同じクラスインスタンスへの参照になります。したがって、次の式の値はtrueでなければなりません。

("a" + "b" + "c").intern() == "abc"

文字列リテラルを導出するために、Java仮想マシンはCONSTANT_String_info構造体によって与えられたコードポイントのシーケンスを調べます。

  • メソッドString.internが、CONSTANT_String_info構造体によって与えられたものと同一のUnicodeコードポイントのシーケンスを含むクラスStringのインスタンスで以前に呼び出された場合、文字列リテラル導出の結果は、クラスStringの同じインスタンスへの参照になります。

  • それ以外の場合は、CONSTANT_String_info構造体で指定されたUnicodeコードポイントのシーケンスを含むStringクラスの新しいインスタンスが作成されます。そのクラスインスタンスへの参照は、文字列リテラルの派生の結果です。最後に、新しいStringインスタンスのインターンメソッドが呼び出されます。

バイトコード

OpenJDK 7でのバイトコードの実装を確認することも有益です。

逆コンパイルすると:

public class StringPool {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
    }
}

定数プールにあります:

#2 = String             #32   // abc
[...]
#32 = Utf8               abc

main

 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne     42
38: iconst_1
39: goto          43
42: iconst_0
43: invokevirtual #7          // Method java/io/PrintStream.println:(Z)V

方法に注意してください:

  • 0and 3:同じldc #2定数がロードされます(リテラル)
  • 12:新しい文字列インスタンスが作成されます(#2引数として)
  • 35ac通常のオブジェクトとして比較されますif_acmpne

定数文字列の表現は、バイトコードでは非常に魅力的です。

  • 通常のオブジェクト(例)とは異なり、専用のCONSTANT_String_info構造を持っていますnew String
  • 構造体は、データを含むCONSTANT_Utf8_info構造体を指します。これが、文字列を表すために必要な唯一のデータです。

上記のJVMSの引用は、Utf8が同じである場合は常に、によって同一のインスタンスがロードされると述べているようですldc

私はフィールドに対して同様のテストを行いました、そして:

  • static final String s = "abc"ConstantValue属性を介して定数テーブルを指します
  • 非finalフィールドにはその属性はありませんが、 ldc

ボーナス:それを、バイトコードを直接サポートしていない(アナログがない)Integerプールと比較してくださいCONSTANT_String_info


2

文字列の複数比較でequals-comparisonがボトルネックになっている場合にのみ、equalsではなく、internと==-comparisonを調べます。これは、intern()がフリーではないため、少数の比較を支援する可能性はほとんどありません。文字列を積極的にインターンした後、intern()への呼び出しが次第に遅くなることがわかります。


2

subString()ソースストリングと比較して結果が小さく、オブジェクトの寿命が長い場合、を使用すると、一種のメモリリークが発生する可能性があります。

通常の解決策は使用new String( s.subString(...))することですが、潜在的/可能性の高い結果を格納するクラスがsubString(...)あり、呼び出し元を制御できない場合はintern()、コンストラクターに渡されるString引数のを格納することを検討できます。これにより、潜在的な大きなバッファが解放されます。


興味深いですが、おそらくこれは実装に依存しています。
akostadinov 2013

1
上記の潜在的なメモリリークは、Java 1.8および1.7.06(以降)では発生しません。Java1.7.0_06で行われたStringの内部表現の変更を参照してください。
eremmel 2014年

これにより、パフォーマンスやメモリのプロファイリング後に必要な場合にのみマイクロ最適化が適用されます。ありがとうございました。
akostadinov 2014年

2

文字列インターンは、equals()メソッドが頻繁に呼び出されている場合に便利ですequals()。メソッドの最初のオブジェクトが同じかどうかをすばやく確認するためです。

if (this == anObject) {
    return true;
}

これは通常Collection、他のコードでも文字列の等価性チェックを実行する可能性がありますが、検索時に発生します。

ただし、インターンにはコストがかかります。コードのマイクロベンチマークを実行したところ、インターンプロセスによりランタイムが10倍に増加することがわかりました。

インターンを行うのに最適な場所は、通常、コード内の文字列が自動的にインターンされるため、コードの外部に格納されているキーを読み取る場合です。これは通常、最初のユーザーのペナルティを防ぐために、アプリケーションの初期化段階で発生します。

これを実行できるもう1つの場所は、キー検索に使用できるユーザー入力を処理するときです。これは通常、リクエストプロセッサで発生します。インターンされた文字列は渡されることに注意してください。

それを除けば、コードの残りの部分でインターンを行う意味はあまりありません。なぜなら、それは一般的に何の利益も与えないからです。


1

メンテナンスの手間をかけない価値があると投票します。

ほとんどの場合、コードが部分文字列で多くの作業を行わない限り、必要はなく、パフォーマンス上の利点もありません。その場合、Stringクラスは元の文字列とオフセットを使用してメモリを節約します。コードで部分文字列を頻繁に使用している場合は、メモリ要件が急増するだけだと思います。


1

http://kohlerm.blogspot.co.uk/2009/01/is-javalangstringintern-really-evil.html

によると、以前にオブジェクトを比較するためにString.equals()使用"=="するアサートString

http://www.codeinstructions.com/2009/01/busting-javalangstringintern-myths.html

文字列の長さを比較してから、内容を比較します。

(ちなみに、販売カタログの製品コード文字列はすべて同じ長さになりがちです-BIC0417は自転車の安全ヘルメットです。TIG0003は生きている大人の男性の虎です-それらの1つを注文するには、おそらくあらゆる種類のライセンスが必要です。そして安全ヘルメットを同時に注文した方がいいかもしれません。)

つまり、文字intern()列をバージョンで置き換えることでメリットを得られるように聞こえますが、equals()プログラミングで「==」を使用せずに、安全性と読みやすさ、標準への準拠を得ることができます。そして、私が言おうとしていることのほとんどは、それが真実である場合、それが真実であることに依存しています。

しかしString.equals()、使用する前に、他のオブジェクトではなく文字列を渡したことをテストします"=="か?私は言う資格がありませんが、そうではないと思います。圧倒的にそのようなequals()操作のほとんどは文字列から文字列になるため、ほとんどの場合テストに合格します。実際、「==」の内部で優先順位を付けるString.equals()ことは、文字列を同じ実際のオブジェクトと頻繁に比較しているという確信を意味します。

次の行が「false」の結果を生成することに誰も驚かないことを願っています。

    Integer i = 1;
    System.out.println("1".equals(i));

しかし、2行目でに変更iするi.toString()と、もちろんtrueです。

あなたがインターンからの利益を期待するかもしれない場所には、明らかに、SetとがありMapます。インターンされた文字列のハッシュコードがキャッシュされていることを願っています...それは要件だと思います。そして、私が100万ドルを稼ぐことができるアイデアを与えただけではないことを願っています。:-)

メモリに関しては、文字列のボリュームが大きい場合、またはプログラムコードが使用するメモリを非常に小さくしたい場合は、これが重要な制限であることも明らかです。-distinct- Stringsのボリュームが非常に大きい場合は、専用のデータベースプログラムコードを使用してそれらを管理し、別のデータベースサーバーを使用することを検討する必要がある場合があります。同様に、小さなプログラム(同時に10000のインスタンスで実行する必要がある)を、文字列自体をまったく保存しないようにすることで改善できる場合。

新しい文字列を作成してすぐにそのintern()代わりに破棄するのは無駄です。しかし、重複する文字列を保持する以外に明確な代替方法はありません。したがって、実際の実行コストは、インターンプールで文字列を検索し、ガベージコレクターが元の文字列を破棄できるようにすることです。そしてそれが文字列リテラルであれば、とにかくすでにインターンされています。

intern()悪意のあるプログラムコードによって悪用されて、Stringとそのオブジェクト参照がすでにintern()プールに存在するかどうか、したがってJavaセッションの他の場所に存在するかどうか(それが知られていない場合)を検出できるかどうか疑問に思っています。しかし、それは、プログラムコードが既に信頼できる方法で使用されている場合にのみ可能だと思います。それでも、ATM PIN番号を保存および記憶するためにプログラムに含めるサードパーティライブラリについて考慮する必要があります。


0

インターンを使用する本当の理由は上記ではありません。メモリ不足エラーが発生した後に使用できます。典型的なプログラムの多くの文字列は、他の大きな文字列のString.substring()です(100K xmlファイルからユーザー名を取り出すことを考えてください)。Javaの実装では、部分文字列は元の文字列への参照とその巨大な文字列のstart + endを保持します。(その背後にある考えは、同じ大きな文字列の再利用です)

1000個の短いファイルを保存するだけの1000個の大きなファイルの後、1000個のファイル全体をメモリに保持します。解決策:このシナリオでは、smallsubstring.intern()を使用するだけです


必要に応じて、部分文字列から新しい文字列を作成しないでください。
–ThorbjørnRavn Andersen 2012

0

私はメモリを節約するためにインターンを使用しています。メモリに大量の文字列データを保持し、インターンを使用するように移動すると、大量のメモリが節約されました。残念ながら、使用するメモリはかなり少なくなりますが、使用するメモリはヒープではなくPermGenメモリに格納されるため、このタイプのメモリの割り当てを増やす方法をお客様に説明することは困難です。

したがって、メモリ消費を削減するためのintern()に代わるものがあります(==と等しいパフォーマンスの利点は私にとって問題ではありません)


0

それに直面しましょう:主なユースケースシナリオは、データストリームを(入力ストリームを介して、またはJDBC ResultSetから)読み取り、無数の小さな文字列が全体にわたって繰り返される場合です。

以下は、文字列やその他の不変式を内部化するために使用するメカニズムの種類、および実装例を制御するための小さなトリックです。

/**
 * Extends the notion of String.intern() to different mechanisms and
 * different types. For example, an implementation can use an
 * LRUCache<T,?>, or a WeakHashMap.
 */
public interface Internalizer<T> {
    public T get(T obj);
}
public static class LRUInternalizer<T> implements Internalizer<T> {
    private final LRUCache<T, T> cache;
    public LRUInternalizer(int size) {
        cache = new LRUCache<T, T>(size) {
            private static final long serialVersionUID = 1L;
            @Override
            protected T retrieve(T key) {
                return key;
            }
        };
    }
    @Override
    public T get(T obj) {
        return cache.get(obj);
    }
}
public class PermGenInternalizer implements Internalizer<String> {
    @Override
    public String get(String obj) {
        return obj.intern();
    }
}

ストリームまたはResultSetからフィールドを読み取るときによく使用します。注:LRUCacheはに基づく単純なキャッシュですLinkedHashMap<K,V>retrieve()すべてのキャッシュミスに対して、ユーザー指定のメソッドを自動的に呼び出します。

これを使用する方法はLRUInternalizer、読み取り(または複数の読み取り)の前に作成し、それを使用して文字列と他の小さな不変オブジェクトを内部化してから解放することです。例えば:

Internalizer<String> internalizer = new LRUInternalizer(2048);
// ... get some object "input" that stream fields
for (String s : input.nextField()) {
    s = internalizer.get(s);
    // store s...
}

0

関連する名前にリンクする約36000のコードのコンテンツをキャッシュするために使用しています。多くのコードが同じ文字列を指しているため、キャッシュ内の文字列をインターンします。

キャッシュ内の文字列を処理することで、同じ文字列を指すコードが実際に同じメモリを指すようにして、RAMスペースを節約しています。

インターンされた文字列が実際にガベージコレクションされた場合、それは私にはまったく機能しません。これは基本的にインターンの目的を無効にします。私はキャッシュ内のすべての文字列への参照を保持しているので、鉱山はガベージコレクションされません。


いいえ、特定の時間にメモリ内にあるすべてのインターンされた等しい文字列は、同じ1つのオブジェクトのままです。これは、ガベージコレクションの前にメモリにあった同等の文字列とは異なるオブジェクトになります。しかし、古い文字列はもう存在しないため、これは問題ありません。
bdruemen

0

文字列をインターンするコストは、単一のstringA.equals(B)比較で節約される時間よりもはるかに多くなります。同じパフォーマンスの文字列変数を繰り返し使用している場合にのみ(パフォーマンス上の理由から)使用してください。たとえば、文字列の安定したリストを定期的に反復して、同じ文字列フィールドをキーとするいくつかのマップを更新すると、かなりの節約になります。

コードの特定の部分を最適化するときは、文字列インターニングを使用してパフォーマンスを調整することをお勧めします。

また、Stringは不変であり、愚かな間違いをしないことも忘れないでください。

String a = SOME_RANDOM_VALUE
a.intern()

忘れずに

String a = SOME_RANDOM_VALUE.intern()

0

String.internの無制限の置き換え、ガベージコレクションも探している場合は、次の方法でうまくいきます。

private static WeakHashMap<String, WeakReference<String>> internStrings = new WeakHashMap<>();
public static String internalize(String k) {
    synchronized (internStrings) {
        WeakReference<String> weakReference = internStrings.get(k);
        String v = weakReference != null ? weakReference.get() : null;
        if (v == null) {
            v = k;
            internStrings.put(v, new WeakReference<String>(v));
        }
        return v;
    }
}

もちろん、存在するさまざまな文字列の数を大まかに見積もることができる場合は、-XX:StringTableSize = highEnoughValueを指定してString.intern()を使用するだけです


SoftRefはもっと意味をなすでしょう。
VACH

@vach(SoftReferenceの代わりに)WeakReferenceを使用することにより、メモリが早く解放されるため、他の割り当てが高速になる可能性があります。それは、アプリケーションが他に何をしているかに依存しますが、どちらかが理にかなっています。
bdruemen
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.