http://javarevisited.blogspot.jp/2015/12/when-to-use-intern-method-of-string-in-java.html をテキトーに訳した
When to use intern() method of String in Java?
String.intern()
メソッドはJavaにおけるStringの重複問題の解決に使われます。String.intern()
メソッドを注意深く使うことで、重複しているStringインスタンスが消費する多くのメモリを節約出来ます。文字列の重複は、同じ内容で異なる文字列が異なるメモリロケーションを占有する場合に発生します。例えば、str1 != str2
なもののstr1.equals(str2)
がtrueの場合などです。Stringオブジェクトは一般的なJavaアプリケーションではヒープメモリの大半を消費するため、重複を減らすためにintern()メソッドを使い、Javaが提供するStringプール機能(String pool feature)を活用することには意味があります。Stringオブジェクトをinternするにはintern()メソッドを使用し、Stringオブジェクトは将来の再利用に備えてStringプールに保存されます。例えば、Stringリテラルを作成する場合、"abc
"などは、自動的にStringプール内に保存されますが、new String("abc")
などで新規にStringオブジェクトを生成する場合、同一のStringではあるものの、新規オブジェクトは別のメモリロケーションに生成されます。これが文字列の重複です。先の新規オブジェクトでintern()
を呼ぶことで、プール内にこのオブジェクトを入れるようにJVMに指示が可能で、それ以降"abc
"を生成する場合には、新規オブジェクトを生成するのではなくプール内のオブジェクトが戻されます。この方法により、プログラム内のStringの重複度合いに応じて、Javaのメモリを削減可能です。なお、Javaパフォーマンス問題のトラブルシューティングとJavaアプリケーションのパフォーマンス最適化についてより深く学ぶには、Java Performance by Charlie huntやScott Oaks氏のJava Performance, The Definitive Guide*1を参照してください。
Some important things about String.intern() method
以下にjava.lang.Stringのintern()メソッドについて覚えておく価値のある重要なポイントについて述べます。
1) String.intern()はJDK 1.1からStringクラスに存在します。このメソッドはStringオブジェクトの標準的な表現(canonical representation)を戻します。internメソッドが呼ばれると、Stringプールにequals()がtrueを返すようなStringオブジェクトが既に含まれる場合、プールからStringオブジェクトを戻し、そうでない場合は、一意なStringのプールにそのオブジェクトを追加します。
2) s1.equals(s2)であるs1
およびs2
でintern()メソッドを呼ぶと、s1.intern() == s2.intern()
となり、これはプール内で同一のString定数を両方のオブジェクトが指すためです。
3) Java 6より前のバージョンでは、String.intern()
の乱用(uncontrolled usage)はjava.lang.OutOfMemory: PermGen spaceを引き起こしがちでした。Stringプールの実体はJavaヒープのPermGenに配置されていて、大半のJVMで領域は極めて小さい(32Mから96M)か固定です。Java 7以降では、intern()メソッドは扱いやすくなっており、これはStringプールがJVMのヒープスペースに移動したためです。これによりString.intern()
メソッドでStringの重複を更に減らせるようになりました。
4) intern()メソッドは非staticメソッドでStringリテラルあるいはStringオブジェクトを使用して呼びます。
以上がJavaでString.intern()メソッドを使う場合に必要なポイントです。また、Java 8 update 20ではString deduplicationと呼ばれる新機能を導入しており、一行も書くことなくStringの重複によるメモリフットプリントの縮小を可能としますが、G1ガベージコレクションでのみ利用可能で、ConcurrentMarkSweepガベージコレクターを使う場合には利用出来ません。