String プールと intern() メソッドが Java でどのように機能するかを理解しています。ここで簡単に紹介します。
Java 6 の String.intern()
古き良き時代、インターンされたすべての文字列は PermGen に格納されていました。これは、ロードされたクラスと文字列プールを格納するために主に使用されるヒープの固定サイズの部分です。明示的にインターンされた文字列に加えて、PermGen 文字列プールには、プログラムで以前に使用されたすべてのリテラル文字列も含まれていました (ここで重要な言葉が使用されます。クラスまたはメソッドがロード/呼び出されなかった場合、その中で定義された定数はロードされません)。
Java 6 のこのような文字列プールの最大の問題は、その場所 (PermGen) でした。PermGen は固定サイズで、実行時に拡張できません。-XX:MaxPermSize=96m オプションを使用して設定できます。私の知る限り、デフォルトの PermGen サイズは、プラットフォームによって 32M から 96M の間で異なります。サイズを大きくすることはできますが、サイズは固定されたままです。このような制限により、String.intern を非常に慎重に使用する必要がありました。このメソッドを使用して制御されていないユーザー入力をインターンしない方がよいでしょう。そのため、Java 6 の時点での文字列プーリングは、ほとんどが手動で管理されたマップに実装されていました。
Java 7 の String.intern()
Oracle のエンジニアは、Java 7 の文字列プール ロジックに非常に重要な変更を加えました。文字列プールはヒープに再配置されました。これは、別の固定サイズのメモリ領域によって制限されなくなったことを意味します。すべての文字列は、他のほとんどの通常のオブジェクトと同様にヒープに配置されるようになりました。これにより、アプリケーションのチューニング中にヒープ サイズのみを管理できます。技術的には、これだけでも、Java 7 プログラムで String.intern() を使用することを再検討する十分な理由になる可能性があります。しかし、他にも理由があります。
この時点までは、このツールJava Visualizerに出会うまで、文字列がメモリに格納される方法に慣れていました。プログラムでメモリがどのように割り当てられているかを視覚化するために、次の単純な Java クラスを作成しました。
public class A {
String iWillAlwaysBeOnHeap="I am on heap...!!!";
class Dummy{
private int dummyNumber;
Dummy(int dummyNumber)
{
this.dummyNumber=dummyNumber;
}
}
public static void main(String[] args) {
A a=new A();
a.lonelyMethod();
}
public void lonelyMethod()
{
String lostString="dangling";
String test=new String("dangling");
test.intern();
String duplicateLiteral="dangling";
Dummy dummy=new Dummy(4);
}
}
そして、私は次の結果を得ました:
ご覧のとおり、同じ値を持つ文字列リテラルとオブジェクトが繰り返されてスタックに格納され、メソッド ローカル文字列のヒープ スペースは考慮されません。最初は戸惑いましたが、調べてみると、JDK 7 が自動的に実行するエスケープ解析があることを知りました。しかし、私のコードでは、ヒープに格納する必要がある String オブジェクトを作成しましたが、ビジュアライザーの出力でわかるようにスタック上にありますが、ダミー クラス オブジェクトはヒープに格納されています。私はこの行動を本当に理解できませんでした。メソッドのローカル文字列は、他のオブジェクトやインスタンス レベルの文字列とはどのように異なる方法で扱われますか?