4

次のようなHashMapがある場合:

HashMap<String, MyObject>

キーがStringのフィールドであるMyObject場合、この文字列値は2回保存されますか?

したがって、エントリを追加すると、次のようになります。

_myMap.put(myObj.getName(), myObj);

メモリの観点から文字列サイズの2倍を使用していますか?それとも、Javaは舞台裏で何か賢いことをしますか?

ありがとう

4

3 に答える 3

16

で新しい文字列値を実際に作成しているのでない限り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ます。

于 2010-03-13T11:13:57.893 に答える
4

Javaは参照を使用するため、2回格納される文字列へのポインタにすぎません。したがって、文字列が巨大であるかどうかを心配する必要はありません。それでも、使用されるメモリの量は同じです。

于 2010-03-13T09:52:30.227 に答える
1

文字列は不変ですが、参照渡しは引き続き適用されます。したがって、2倍のメモリを消費することはありません。

于 2010-03-13T09:53:06.573 に答える