Java では、文字列は String クラスのオブジェクトとしてメモリに保存されます。
Java プログラムにメモリが割り当てられると、JVM (Java 仮想マシン) は割り当てられたメモリを 2 つの部分に分割します。 1 つの部分はスタック、もう 1 つの部分はヒープです。ヒープ メモリでは、Java は一部のメモリを割り当てます。特にリテラルに対して、そのメモリは文字列定数プール (SCP) と呼ばれます。 SCP は、ヒープ内の事前定義された領域です。文字列プールは、Java ランタイム用のスペースを大幅に節約するのに役立ちます。 String クラスは SCP を使用して一意の文字列リテラルを保存します。
スタック メモリには、変数、変数参照、またはオブジェクトへの参照が保存されます。
ヒープメモリには、動的に割り当てられたすべてのオブジェクトが格納されます。オブジェクトにメモリを割り当てるには、新しいキーワードを使用します。
文字列オブジェクトを作成するには 2 つの方法があります。
文字列 str1 = “MyString”;
文字列リテラルを作成するときは常に、JVM はまず文字列リテラルが文字列定数プールにすでに存在するかどうかを確認します。利用できない場合は、SCP に新しい文字列リテラルが作成されます。
上の図では、str1 は SCP の「MyString」を指しています。新しく作成された文字列リテラルの処理方法は次のとおりです。
String str2 = new String(“MyString”); //新しいキーワードを使用して文字列クラスをインスタンス化します
新しいキーワードを使用して文字列オブジェクトを作成すると、2 つのオブジェクトが作成されます。 1 つは SCP に、もう 1 つはヒープにあり、参照変数はスタックに保存されます。
を使用してリテラルの「MyString」をすでに作成しています。文字列 str1 = “MyString”;
SCP 内に重複を持たせることはできないため、JVM は SCP 内にもう 1 つのオブジェクトを作成しませんが、スタック内の変数 str3 への既存の参照を返し、ヒープ内に 1 つのオブジェクトを作成します。 Str3 はヒープ内のオブジェクト「MyString」を指しますが、SCP 内にはありません。
以下は、文字列オブジェクトにメモリが割り当てられる方法のさまざまなケースです。
ケース 1: 上記で定義された文字列オブジェクトがメモリに格納される方法。
パブリック クラス stringsStorageConcept
{
public static void main(String[] args)
{
文字列 str1 = “MyString”;
String str2 = new String(“MyString”);
System.out.println(str1 == str2); //出力:False
System.out.println(str1.equals(str2)); //出力:True
}
}
「==」演算子を使用して str1 と str2 を比較すると、false が返されます。ご存知のとおり、「==」演算子は物理アドレスを比較します。この例では、str1 は SCP 内のオブジェクトを指し、str2 はヒープ内のオブジェクトを指します。したがって、 false を返します。
しかし、str1.equals(str2) の場合、ご存知のとおり、「equals」関数は個々の文字をチェックし、str1 と str3 の両方に同じ値が格納されているため、true が返されます。
ケース 2: 別の文字列リテラル
文字列 str3 = “MyString”;
str1 と str3 は両方とも、SCP 内の同じ文字列リテラルを指します。
パブリック クラス stringsStorageConcept
{
public static void main(String[] args)
{
文字列 str1 = “MyString”;
文字列 str3 = “MyString”;
System.out.println(str1 == str2); //出力:True
System.out.println(str1.equals(str2)); //出力:True
}
}
s1 == s3 は、「==」演算子が物理アドレスを比較するが内容は比較しないため、true を返します。
s1.equals(s3) は true を返し、「equals」関数は両方の参照変数の個々の文字をチェックします。
ケース 3: 新しいキーワードを使用して別の文字列オブジェクトが作成されます
String str4 = new String(“NewString”);
この場合、JVM は SCP でこの文字列をチェックします。値「NewString」を持つ文字列オブジェクトが見つからないため、SCP とヒープに 2 つのオブジェクトを作成し、参照変数 str4 は次の場所に保存されます。スタック。 Str4 にはヒープ内のオブジェクトへの参照が含まれます。
ケース 4: 別の文字列リテラルが作成されます。
文字列 str5 = “NewString”;
この場合、JVM はこのリテラルがすでに利用可能かどうかを SCP でチェックします。ここでは「NewString」が SCP にすでに存在しているため、JVM は SCP に重複を作成せず、代わりに変数 str5 への参照を返します。
ケース 5: ある文字列を別の文字列に代入する
String str4 = new String(“NewString”);
文字列 str6 = str4; //代入
ここで、str6 と str4 はヒープ内の同じオブジェクトを指し、str4 の値は消去されません。
パブリック クラス stringsStorageConcept
{
public static void main(String[] args)
{
String str4 = new String(“NewString”);
文字列 str6 = str4;
System.out.println(str4 == str6); //出力: true
}
}
JVM は、ヒープ内の「NewString」の参照を変数 str6 に与えます。これが、str4 == str6 が true を返す理由です。
結論として、文字列リテラルを使用し、「new」演算子によって文字列オブジェクトを作成することには、長所と短所があります。
文字列リテラルを使用すると、重複を作成せずにメモリをより効率的にすることができます。 JVM は 1 つの一意のオブジェクトを作成し、文字列は SCP 内に永久に残ります。この欠点は、文字列プールのサイズが固定されており、いつかいっぱいになることです。
しかし、新しいキーワードを使用すると、2 つのオブジェクトが作成され、1 つは SCP に、もう 1 つはヒープに作成されます。ヒープでは、オブジェクトが必要ない場合、スペースを作るためにガベージ コレクターによって消去されます。しかし、この欠点は、「new」演算子を使用すると、JVM が常に新しいオブジェクトを作成する必要があり、JVM にとって過負荷になることです。
以上がJava での文字列のメモリ割り当ての詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。