8

Java のオブジェクトのメモリ使用量を理解しようとしています。Javaのオブジェクトとメモリに関するこれと他のドキュメントを読みました。

ただし、sizeof Java ライブラリまたは visualvm を使用している場合、2 つの異なる結果が得られますが、前の参照 ( http://www.javamex.com ) によると、どちらも期待できるものではありません。

私のテストでは、Java SE 7 Developer Previewon64-bits Macjava.sizeof 0.2.1を使用していvisualvm 1.3.5ます。

TestObject、、、の 3 つのクラスがTestObject2ありTestObject3ます。

public class TestObject
{

}

public class TestObject2 extends TestObject
{
    int a = 3;
}

public class TestObject3 extends TestObject2
{
    int b = 4;
    int c = 5;
}

私の主なクラス:

public class memoryTester
{
    public static void main(String[] args) throws Throwable
    {
        TestObject object1 = new TestObject();
        TestObject2 object2 = new TestObject2();
        TestObject3 object3 = new TestObject3();

        int sum = object2.a + object3.b + object3.c;
        System.out.println(sum);

        SizeOf.turnOnDebug();

        System.out.println(SizeOf.humanReadable(SizeOf.deepSizeOf(object1)));
        System.out.println(SizeOf.humanReadable(SizeOf.deepSizeOf(object2)));
        System.out.println(SizeOf.humanReadable(SizeOf.deepSizeOf(object3)));
    }
}

java.SizeOf() を使用すると、次のようになります。

{ test.TestObject
} size = 16.0b
16.0b

{ test.TestObject2
 a = 3
} size = 16.0b
16.0b

{ test.TestObject3
 b = 4
 c = 5
} size = 24.0b
24.0b

私が持っているvisualvmで:

this (Java frame)   TestObject  #1  16
this (Java frame)   TestObject2 #1  20
this (Java frame)   TestObject3 #1  28

インターネットで読んだドキュメントによると、私は 64 ビットなので、16 バイトのオブジェクト ヘッダーが必要ですTestObject

次にTestObject2、整数フィールドに 4 バイトを追加して 20 バイトにする必要があるため、4 バイトのパディングを再度追加して、合計サイズを 24 バイトにする必要がありTestObject2ます。私が間違っている?

についてもTestObject3同様に、2 つの整数フィールドにさらに 8 バイトを追加する必要があります。これは 32 バイトになるはずです。

VisualVm はパディングを無視するようですが、java.sizeOf はオブジェクト ヘッダーに含まれているかのように 4 バイトを欠いているようです。整数を 4 つのブール値に置き換えると、同じ結果が得られます。

質問:

これら 2 つのツールで異なる結果が得られるのはなぜですか?

パディングが必要ですか?

また、クラスとそのサブクラスの間にパディングがある可能性があることをどこかで読みました(リンクが見つかりませんでした)、そうですか?その場合、継承されたクラスのツリーにメモリのオーバーヘッドが発生する可能性がありますか?

最後に、Java が行っていることの詳細を示す Java 仕様/ドキュメントはありますか?

ご協力いただきありがとうございます。

アップデート:

utapyngo のコメントに答えるために、visualvm のオブジェクトのサイズを取得するために、heapdump を作成し、「クラス」の部分で、「インスタンス」列の次の「サイズ」列を確認します。オブジェクトの種類ごとに 1 の場合のインスタンスの数。

Nathaniel Ford のコメントに答えるために、各フィールドを初期化し、メインメソッドでそれらを使用して単純な合計を計算しました。結果は変わりませんでした。

4

1 に答える 1

2

はい、パディングが発生する可能性があります。スタック上のオブジェクトが完全に最適化される可能性もあります。いつでも正確なサイズを知っているのは JVM だけです。Java 言語内からサイズを概算するこのような手法はすべて意見が一致しない傾向があるため、JVM に付属するツールが最も正確である傾向があります。私が認識している Java 内で sizeOf を実装する 3 つの主な手法は次のとおりです。

  1. オブジェクトをシリアル化し、それらのバイトの長さを返します (明らかに間違っていますが、相対的な比較には役立ちます)
  2. リスト アイテム リフレクション、およびオブジェクトで見つかった各フィールドのハード コーディングされたサイズ定数。ちょっと正確になるように調整できますが、JVM の変更と、JVM が行っているかどうかに関係なくパディングがスローされます。
  3. リスト項目はオブジェクトの負荷を作成し、gc を実行し、jvm ヒープ サイズの変化を比較します

これらの手法はどれも正確ではありません。

v1.5 以降の Oracle JVM で実行している場合。次に、Java ランタイムが使用する C 構造体から直接オブジェクトのサイズを読み取る方法があります。本番環境にはお勧めできません。間違えると、JVM がクラッシュする可能性があります。しかし、これを試してみたい場合は、興味深いと思われるブログ投稿を次に示します: http://highlyscalable.wordpress.com/2012/02/02/direct-memory-access-in-Java/

Java が実際に行っていることに関するドキュメントに関しては、それは JVM 固有であり、バージョン固有であり、場合によっては構成固有でもあります。各実装は、オブジェクトを異なる方法で自由に処理できます。たとえば、オブジェクトを完全に最適化する程度であっても、スタックから渡されないオブジェクトは自由にヒープに割り当てられません。一部の JVM では、オブジェクトを CPU レジスタ内に完全に保持することさえできます。ここではあなたのケースではありませんが、Java オブジェクトの実際のサイズを取得するのが難しい理由の例としてこれを含めます。

したがって、ひとつまみの塩で取得した sizeOf 値を取得し、それを「ガイドライン」の測定値としてのみ扱うのが最善です。

于 2013-03-19T08:28:18.347 に答える