次のようなHashMapがある場合:
HashMap<String, MyObject>
キーがString
のフィールドであるMyObject
場合、この文字列値は2回保存されますか?
したがって、エントリを追加すると、次のようになります。
_myMap.put(myObj.getName(), myObj);
メモリの観点から文字列サイズの2倍を使用していますか?それとも、Javaは舞台裏で何か賢いことをしますか?
ありがとう
次のようなHashMapがある場合:
HashMap<String, MyObject>
キーがString
のフィールドであるMyObject
場合、この文字列値は2回保存されますか?
したがって、エントリを追加すると、次のようになります。
_myMap.put(myObj.getName(), myObj);
メモリの観点から文字列サイズの2倍を使用していますか?それとも、Javaは舞台裏で何か賢いことをしますか?
ありがとう
で新しい文字列値を実際に作成しているのでない限りgetName()
、メモリ使用量を複製しているわけではありません。
物事を明確にするためのいくつかの例を次に示します。
String s1 = "Some really long string!";
String s2 = s1;
assert s1.equals(s2);
ここで、s1 == s2
; それらは同じString
インスタンスを参照します。メモリ使用量は、2つの参照変数(大したことではありません)、1String
つのインスタンス、および1つのバッキングchar[]
(メモリを占有する部分)です。
String s1 = "Some really long string!";
String s2 = new String(s1);
assert s1.equals(s2);
ここで、s1 != s2
; それらは異なるString
インスタンスを参照します。ただし、文字列は不変であるため、コンストラクターは、文字列が同じ文字配列を共有できることを認識しています。メモリ使用量は、2つの参照変数、2つのString
インスタンス(まだ大したことではないので...)、および1つのバッキングchar[]
です。
String s1 = "Some really long string!";
String s2 = new String(s1.toCharArray());
assert s1.equals(s2);
ここでは、前と同じように、s1 != s2
。別のコンストラクターが使用されますが、今回はchar[]
代わりに使用します。不変性を確保するには、内部配列toCharArray()
の防御コピーを返す必要があります(そうすれば、返された配列に変更を加えても文字列値が変更されません)。
[ toCharArray()は]新しく割り当てられた文字配列を返します。その長さはこの文字列の長さであり、その内容はこの文字列で表される文字シーケンスを含むように初期化されます。
さらに悪いことに、コンストラクターは、不変性を確保するために、指定された配列を内部のバッキング配列に防御的にコピーする必要もあります。これは、文字配列の最大3つのコピーが同時にメモリに存在する可能性があることを意味します。それらのうちの1つは最終的にガベージコレクションされるため、メモリ使用量は2つの参照変数、2つString
のインスタンス、および2つのバッキングchar[]
です。今、あなたのメモリ使用量は2倍になります!
したがって、質問に戻って、で新しい文字列値を作成していない限りgetName()
(つまり、単にreturn this.name;
)、問題はありません。ただし、単純な連結を行っている場合(たとえばreturn this.firstName + this.lastName;
)、メモリ使用量は2倍になります。
次のコードは私のポイントを示しています。
public class StringTest {
final String name;
StringTest(String name) {
this.name = name;
}
String getName() {
return this.name; // this one is fine!
// return this.name + ""; // this one causes OutOfMemoryError!
}
public static void main(String args[]) {
int N = 10000000;
String longString = new String(new char[N]);
StringTest test = new StringTest(longString);
String[] arr = new String[N];
for (int i = 0; i < N; i++) {
arr[i] = test.getName();
}
}
}
java -Xmx128m StringTest
最初に、例外をスローせずに上記のコードが実行されることを確認する必要があります( )。次に、に変更getName()
しreturn this.name + "";
て再度実行します。今回はを取得しOutOfMemoryError
ます。
Javaは参照を使用するため、2回格納される文字列へのポインタにすぎません。したがって、文字列が巨大であるかどうかを心配する必要はありません。それでも、使用されるメモリの量は同じです。
文字列は不変ですが、参照渡しは引き続き適用されます。したがって、2倍のメモリを消費することはありません。