1

Java では、オブジェクトが割り当てられている間に使用するメモリの正確な量を把握したいと考えています。

コンストラクターとメジャーを呼び出すだけでは、コンストラクター中に他のオブジェクトが割り当てられる可能性があるため、機能しません。また、特定の VM で使用されているメモリをリアルタイムで計算する方法を使用することを好みます。これは標準の VM ではない可能性があるため、フィールドを数えて適切な推測を行うだけでは十分ではありません。

とにかく、これまでのところ、見つかった を使用して、他の割り当てなしでオブジェクトを作成できることがnewConstructorForSerializationわかりましたsun.reflect.ReflectionFactory

これは機能しますが、どういうわけか、newInstance の呼び出しは、予想よりも多くの 1 ブロックのメモリを割り当てます。

たとえば、クラス

public class a {
    Integer a;
}

public class b {
    Integer b = new Integer(12345);
}

どちらも同じ結果になるはずです。この場合、デフォルト VM で Java 7 を使用して 16 バイト。

ただし、私のコードでは 32 バイト (常に予想よりも 16 バイト多い) が返されます。結果から 16 を削除することでこれを補うことができますが、常に追加のブロックが割り当てられることを 100% 確認する必要があります。正確な量よりも、メモリ使用量の上限を知ることが重要です。したがって、このブロックが常に追加されると 100% 確信している場合にのみ、結果から 16 を差し引いても安全です。

私のコード: ( -XX:-UseTLAB VM 引数で実行)

import java.lang.reflect.Constructor;

import sun.reflect.ReflectionFactory;

public class test {

    public static void main(String[] args) throws Exception {
        prepare(a.class, Object.class);
        System.out.println(memUse());
        System.out.println(memUseSimple());
    }

    private static long memUseSimple() {
        long start = Runtime.getRuntime().freeMemory();
        a a = new a();
        return start - Runtime.getRuntime().freeMemory();
    }

    private static long memUse() throws Exception {
        Object o0 = intConstr.newInstance();
        long start = Runtime.getRuntime().freeMemory();
        Object o1 = intConstr.newInstance();
        return start - Runtime.getRuntime().freeMemory() - 16;
    }

    private static Constructor<?> intConstr;

    private static void prepare(Class<?> clazz, Class<?> parent) throws Exception {
            intConstr = ReflectionFactory.getReflectionFactory()
                    .newConstructorForSerialization(clazz,
                            parent.getDeclaredConstructor());
            return;
    }
}

編集:明確にするために:呼び出しの16バイトのオーバーヘッドを差し引く必要がある理由を知りたいのですが、intConstr.newInstance()このオーバーヘッドが常に同じ(または少なくとも16バイト以上)であると100%確信できる場合。

上記のコードで a を b に置き換えても、 の結果として 16 が得られますが、 は得られmemUse()ませんmemUseSimple()。気にするだけですmemUse()が、比較として簡単な方法を追加しました。

intConstr.newInstance()別の VM で異なるオーバーヘッドが発生する可能性があることはわかっています。これは重要ではありません。現在の VM で 16 バイトのオーバーヘッドが発生する場合、(このランタイム中に) 常に 16 バイトのオーバーヘッドが発生するかどうかを知る必要がありますか? また、このオーバーヘッドはただと比較してどこから来るのnew a()でしょうか?

4

1 に答える 1

1

事は多かれ少なかれ単純です。new次のコードを使用して、 と の間に違いがあるかどうかを確認しましたnewInstance()(Java 7、64 ビット、-XX:-UseTLAB -verbose:gc):

public final static long usedMemory()
{
    return Runtime.getRuntime().totalMemory() 
               - Runtime.getRuntime().freeMemory();
}

public static void main(String[] args) throws Exception
{
    // we get the default ctor
    Constructor<?> ctor = ReflectionFactory
            .getReflectionFactory()
            .newConstructorForSerialization(
                    Main.class, Object.class.getDeclaredConstructor());
    // warm up newInstance
    Object d = ctor.newInstance();
    // warm up Runtime
    System.out.println(usedMemory());
    // warm up Main
    Object b = new Main();
    // force GC
    System.gc();
    // get currently used memory
    final long mem = usedMemory();
    Object a = new Main();
    System.out.println(usedMemory() - mem);
    Object c = ctor.newInstance();
    System.gc();
    System.out.println(usedMemory() - mem);
    System.out.println(a + ", " + b + ", " + c + ", " + d);
}

コードの一部が最適化されないようにコンパイラをだまして、システムがいくつかのバックグラウンド要素を初期化できるようにする必要があるため、コードはこのように見えます。私が得る出力は次のとおりです。

384024
[GC 381K->400K(249664K), 0.0006325 secs]
[Full GC 400K->264K(249664K), 0.0040675 secs]
16
[GC 264K->296K(249664K), 0.0002040 secs]
[Full GC 296K->264K(249664K), 0.0023534 secs]
32

・・・まさに期待通りでした。「Main」タイプのオブジェクトは 16 バイトを消費し、別のオブジェクトを割り当てると 32 バイトを消費します。2 番目の値は、最初の「メイン」割り当ての前に使用されたメモリ値との差です。

また、Mainクラスにメンバー変数が含まれているかどうかにも違いはありませんでした。なし、Integer integer;ありInteger integer = Integer.of(42);、 ありで試してみましたが、結果は常に同じでした。

発生するオーバーヘッドは、newInstance通話のバックグラウンドで起こっていることから生じます。私の例では、GC を強制的に実行することでこれを補いました。

newこれは少しごまかしているかもしれませんが、この例は、 と の間に違いがあることを示すためのnewInstanceものですが、後者はバックグラウンドでより多くのものを割り当てます。これらのオブジェクトは eden 空間に割り当てられ、GC サイクルに耐えられないため、無視することができます。

于 2013-01-15T20:41:48.523 に答える