2

私はのガベージ動作をプロファイリングしていますが、クラス内で初めてjava.lang.String文字列をインスタンス化するたびに、常にガベージが生成されるようです。誰かが理由を知っていますか?

public abstract class AbstractTest {

    protected static String SERGIO = "sergio";

    private String someValue;

    public void init() {
        this.someValue = new String(SERGIO);
    }
}

public class Test extends AbstractTest {

    private static String JULIA = "julia";

    private Runtime runtime = Runtime.getRuntime();
    private String anotherValue;
    private String yetAnother;

    private void gc() throws InterruptedException {
        System.gc();
        Thread.sleep(100);
    }

    private long usedMemory() {
        return runtime.maxMemory() - runtime.freeMemory();
    }

    public void test() throws Exception {
        gc();
        this.anotherValue = new String(SERGIO); // a bunch of garbage is created!
        long usedMemory = usedMemory();
        gc();
        long usedMemoryAfterGC = usedMemory();
        System.out.println("Collected: " + (usedMemory - usedMemoryAfterGC));
        gc();
        this.yetAnother = new String(JULIA); // no more garbage
        usedMemory = usedMemory();
        gc();
        usedMemoryAfterGC = usedMemory();
        System.out.println("Collected: " + (usedMemory - usedMemoryAfterGC));
    }

    public static void main(String[] args) throws Exception {
        Test t = new Test();
        t.test();
    }

出力:

収集:704336
収集:0

それはいいです。最初にガベージを作成し、その後のインスタンス化ではガベージを生成しません。

奇妙なことに、スーパークラスで文字列を強制的に作成しても、サブクラスで文字列を最初にインスタンス化したときに、サブクラスでガベージが作成されます。

public void test() throws Exception {
    gc();
    init(); // creates a String in the superclass
    gc();
    this.yetAnother = new String(JULIA);
    long usedMemory = usedMemory();
    gc();
    long usedMemoryAfterGC = usedMemory();
    System.out.println("Collected: " + (usedMemory - usedMemoryAfterGC));
}

出力:

収集:348648

なぜですか?

(ちなみに、これはMACとJDK 1.6.0_37で実行しています)

編集1:コードを少し変更して、文字列の内部化がここでの原因ではないことを明確にしました。少なくとも、そうであるようには見えません。

EDIT2:コード全体でStringをObjectに変更すると、同じガベージが発生するため、Javaでnewを介したオブジェクトの割り当てがどのように行われるかに関係していると思います。クラスに初めてオブジェクトを割り当てると、ガベージが発生します。2回目はしません。奇妙なことに、クラスごとです。

EDIT3:上記のコードで行っているように、GCにガベージ作成のためにアプリケーションのプロファイルを強制する方法について説明するブログ記事を書きました。

4

2 に答える 2

3

クラス内のリテラル文字列は、最初の参照時に「インターン」されます(以前でない場合)。通常、インターンには元のバージョンの文字列を破棄し、インターンされたバージョンを使用する必要があります。その過程で、さらにいくつかのオブジェクトが作成され、破棄される場合があります。

(もちろん、特別な内部フックがなければ、1回の操作で生成されているガベージの量を検出する信頼できる方法は実際にはありません。したがって、測定値はせいぜい疑わしいものです。)

于 2012-11-21T21:16:35.043 に答える
1

ここでピーターの答えを読んだ後、TLABが犯人であることは明らかです。このオプションを使用して-XX:-UseTLABTLABを無効にすると、問題は解決します。ここから理解したことから、TLABでは、スレッドが最初にメモリの大きなチャンクを割り当てて、後で競合状態を回避するように見えます。-XX:TLABSize=64mでTLABのサイズを大きく設定し、この金額が割り当てられていることを確認することで、TLABが原因であることを証明できました。

于 2012-11-23T18:50:52.903 に答える