20

短いバージョン: string.substring(n,m).intern() を呼び出す場合、文字列テーブルは部分文字列または元の文字列を保持しますか?

...しかし、それが正しい質問であるかどうかはわかりません。そのため、長いバージョンを次に示します。

私は、ファイルを 1 つの大きな文字列として丸呑みし、String.split、.trim、.substring、および StringTokenizer を使用してそれらをトークンに分解することにより、ファイルを解析する従来の Java コード (PCGen) を使用しています。これらのメソッドはいずれも元の文字列をコピーせず、すべてが共有 char[] の一部を指しているため、これは解析に非常に効率的です。

解析が終わったら、メモリを解放したいと思います。元の大きな文字列のいくつかの小さな部分文字列だけが必要ですが、強力な参照によって大きな文字列が収集されなくなります。その後、私は OOM に苦しんでいます。これは、多くの解析済みファイルのヒープへの大きな影響が原因の 1 つだと思います。

new String(String)(コピーオンライト)を介して大きな文字列を削除できることはわかっています。そして、 String.intern を介して文字列の重複を減らすことができることを知っています (解析されたファイルには多くの冗長性があるため、これは重要です)。最大量のヒープを再利用するには両方を使用する必要がありますか?それとも .intern() で両方を行う必要がありますか? OpenJDK7 ホットスポット ソース コード (hotspot/src/share/vm/classfile/symbolTable.cpp) を読むと、文字列テーブルが文字列全体を保持し、オフセット/長さをまったくトリミングしていないように見えます。そのため、新しい文字列を作成し、その結果をインターンする必要があると思います。右?

そうは言っても、ストリーミング パーサーに切り替えることは、メモリの観点からは大きなメリットですが、短期的には大きすぎる変化です。

4

2 に答える 2

10

new String(String) および intern() メソッドを使用できます。これにより、Java 7 update 4 までの必要に応じてコピーが取得されます。Java 7 update 5 から、部分文字列はより深いコピーを取得しますが、引き続き intern を使用することをお勧めします。 (). 注: Java 7 は、perm gen ではなくヒープを使用して文字列リテラルを格納します。

public static void main(String[] args) {
    char[] chars = new char[128];
    Arrays.fill(chars, 'A');
    String a128 = new String(chars);
    printValueFor("a128", a128);
    String a16 = a128.substring(0, 16);
    printValueFor("a16", a16);
}

public static void printValueFor(String desc, String s) {
    try {
        Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        char[] valueArr = (char[]) value.get(s);
        System.out.println(desc + ": " + Integer.toHexString(System.identityHashCode(valueArr)) + ", len=" + valueArr.length);
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

Java 7 update 4 プリント

a128: 513e86ec, len=128
a16: 53281264, len=16

Java 6 はこれを行わないと思います。

于 2013-01-25T06:48:15.150 に答える
3

テストできます。文字列はその文字配列をフィールドに保持します

   private final char value[];

substring(); の後に何が起こるか見てみましょう。インターン();

    Field f = String.class.getDeclaredField("value");
    f.setAccessible(true);
    String s1 = "12345";
    String s2 = s1.substring(1, 2);
    String s3 = s2.intern();
    System.out.println(f.get(s2) == f.get(s1));
    System.out.println(f.get(s3) == f.get(s2));

出力

true
true

つまり、3 つの文字列すべてが同じ文字配列を共有します。

于 2013-01-25T06:59:14.553 に答える