235

100 個の属性を持つ 1 つのオブジェクトが消費するメモリ スペースは、それぞれ 1 つの属性を持つ 100 個のオブジェクトの場合と同じですか?

オブジェクトに割り当てられるメモリの量は?
属性を追加するときに使用される追加のスペースはどれくらいですか?

4

12 に答える 12

194

Mindprodは、これは簡単に答えられる質問ではないことを指摘しています。

JVM は、任意の量のパディングまたはオーバーヘッドを使用して、ビッグ エンディアンまたはリトル エンディアンの任意の方法でデータを内部的に自由に格納できますが、プリミティブは公式サイズを持っているかのように動作する必要があります。
たとえば、JVM またはネイティブ コンパイラはboolean[]、 をBitSet. プログラムが同じ答えを返す限り、それはあなたに言う必要はありません。

  • スタックにいくつかの一時オブジェクトを割り当てる場合があります。
  • 一部の変数またはメソッド呼び出しを最適化して、それらを定数に置き換えて完全に存在させない場合があります。
  • メソッドまたはループをバージョン管理する場合があります。つまり、それぞれが特定の状況に合わせて最適化された 2 つのバージョンのメソッドをコンパイルし、どちらを呼び出すかを前もって決定します。

もちろん、ハードウェアと OS には多層キャッシュ、オンチップ キャッシュ、SRAM キャッシュ、DRAM キャッシュ、通常の RAM ワーキング セット、およびディスク上のバッキング ストアがあります。データはすべてのキャッシュ レベルで複製される可能性があります。この複雑さはすべて、RAM 消費量を非常に大まかにしか予測できないことを意味します。

測定方法

を使用Instrumentation.getObjectSize()して、オブジェクトが消費するストレージの見積もりを取得できます。

実際のオブジェクト レイアウト、フットプリント、および参照を視覚化するには、 JOL (Java Object Layout) ツールを使用できます。

オブジェクト ヘッダーとオブジェクト参照

最新の 64 ビット JDK では、オブジェクトには 12 バイトのヘッダーがあり、8 バイトの倍数にパディングされているため、オブジェクトの最小サイズは 16 バイトです。32 ビット JVM の場合、オーバーヘッドは 8 バイトで、4 バイトの倍数になるようにパディングされます。 ( Dmitry Spikhalskiy の回答、 Jayen の回答、およびJavaWorldから。)

通常、参照は 32 ビット プラットフォームまたは 64 ビット プラットフォームでは 4 バイト-Xmx32Gです。および 32Gb より上の 8 バイト ( -Xmx32G)。 (圧縮されたオブジェクト参照を参照してください。)

その結果、64 ビットの JVM では通常、30 ~ 50% 多いヒープ領域が必要になります。( 32 ビットまたは 64 ビットの JVM を使用する必要がありますか?、2012 年、JDK 1.7)

ボックス化された型、配列、および文字列

ボックス化されたラッパーには、プリミティブ型と比較してオーバーヘッドがあります ( JavaWorldから)。

  • Integer: 16 バイトの結果は、予想よりも少し悪いですint。値が 4 バイト余分に収まるからです。を使用するIntegerと、値をプリミティブ型として格納できる場合と比較して、300% のメモリ オーバーヘッドが発生します。

  • Long: 16 バイトも: 明らかに、ヒープ上の実際のオブジェクト サイズは、特定の CPU タイプの特定の JVM 実装によって行われる低レベルのメモリ アラインメントの影響を受けます。Long8 バイトのオブジェクト オーバーヘッドに加えて、実際の long 値用にさらに 8 バイトあるように見えます。対照的にInteger、未使用の 4 バイト ホールがありました。おそらく、私が使用している JVM が 8 バイト ワード境界でオブジェクトのアライメントを強制するためです。

他のコンテナも高価です。

  • 多次元配列: 別の驚きがあります。
    開発者は通常int[dim1][dim2]、数値計算や科学計算のような構造を採用しています。

    int[dim1][dim2]配列インスタンスでは、ネストされたすべてのint[dim2]配列はObjectそれ自体が です。それぞれ、通常の 16 バイトの配列オーバーヘッドが追加されます。三角形または不規則な配列が必要ない場合、それは純粋なオーバーヘッドを表します。配列の次元が大きく異なると影響が大きくなります。

    たとえば、int[128][2]インスタンスには 3,600 バイトが必要です。int[256]インスタンスが使用する 1,040 バイト (同じ容量) と比較すると、3,600 バイトは 246% のオーバーヘッドを表します。の極端な場合byte[256][1]、オーバーヘッド係数はほぼ 19 です。これを、同じ構文でストレージのオーバーヘッドが追加されない C/C++ の状況と比較してください。

  • String: aStringのメモリの増加は、内部の char 配列の増加を追跡します。ただし、このStringクラスはさらに 24 バイトのオーバーヘッドを追加します。

    サイズが 10 文字以下の空Stringでない場合、有効なペイロード (文字ごとに 2 バイトと長さの 4 バイト) に対する追加のオーバーヘッド コストは、100 ~ 400% の範囲です。

アライメント

この例のオブジェクトを考えてみましょう:

class X {                      // 8 bytes for reference to the class definition
   int a;                      // 4 bytes
   byte b;                     // 1 byte
   Integer c = new Integer();  // 4 bytes for a reference
}

単純な合計では、 のインスタンスがX17 バイトを使用することが示唆されます。ただし、アラインメント (パディングとも呼ばれます) により、JVM は 8 バイトの倍数でメモリを割り当てるため、17 バイトではなく 24 バイトが割り当てられます。

于 2008-11-03T08:42:19.403 に答える
37

アーキテクチャ/jdk に依存します。最新の JDK および 64 ビット アーキテクチャの場合、オブジェクトには 12 バイトのヘッダーと 8 バイトのパディングがあるため、オブジェクトの最小サイズは 16 バイトです。Java Object Layoutと呼ばれるツールを使用して、サイズを決定し、オブジェクトのレイアウトとエンティティの内部構造に関する詳細を取得したり、クラス参照によってこの情報を推測したりできます。私の環境での整数の出力例:

Running 64-bit HotSpot VM.
Using compressed oop with 3-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

java.lang.Integer object internals:
 OFFSET  SIZE  TYPE DESCRIPTION                    VALUE
      0    12       (object header)                N/A
     12     4   int Integer.value                  N/A
Instance size: 16 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

したがって、Integer の場合、4 バイトの int がヘッダーの直後とパディング境界の前に圧縮されるため、インスタンス サイズは 16 バイトになります。

コードサンプル:

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.VMSupport;

public static void main(String[] args) {
    System.out.println(VMSupport.vmDetails());
    System.out.println(ClassLayout.parseClass(Integer.class).toPrintable());
}

Maven を使用している場合、JOL を取得するには:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.3.2</version>
</dependency>
于 2015-08-26T10:43:56.143 に答える
29

各オブジェクトには、関連するモニターとタイプの情報、およびフィールド自体に対して一定のオーバーヘッドがあります。それを超えて、フィールドはほとんどレイアウトできますが、JVMは適切だと思います(私は信じています)-しかし、別の回答に示されているように、少なくとも一部のJVMはかなり密集します。次のようなクラスを考えてみましょう:

public class SingleByte
{
    private byte b;
}

public class OneHundredBytes
{
    private byte b00, b01, ..., b99;
}

32 ビットの JVM では、100 個のインスタンスがSingleByte1200 バイト (オーバーヘッドの 8 バイト + パディング/アラインメントによるフィールドの 4 バイト) を必要とすることが予想されます。の 1 つのインスタンスがOneHundredBytes108 バイト (オーバーヘッド、次に 100 バイト) を占めると予想します。ただし、JVM によって確かに異なる場合があります。ある実装では、フィールドを にパックしないことを決定する場合があり、OneHundredBytes408 バイト (= 8 バイトのオーバーヘッド + 4 * 100 アライメント/パディングされたバイト) が必要になります。64 ビット JVM では、オーバーヘッドも大きくなる可能性があります (不明)。

編集: 以下のコメントを参照してください。どうやら HotSpot は 32 バイト境界ではなく 8 バイト境界までパディングするため、 の各インスタンスはSingleByte16 バイトになります。

いずれにせよ、「単一の大きなオブジェクト」は、少なくとも複数の小さなオブジェクトと同じくらい効率的です - このような単純なケースでは。

于 2008-11-03T08:39:52.233 に答える
8

プログラムの使用済み/空きメモリの合計は、プログラムで次の方法で取得できます

java.lang.Runtime.getRuntime();

ランタイムには、メモリに関連するいくつかのメソッドがあります。次のコーディング例は、その使用法を示しています。

 public class PerformanceTest {
     private static final long MEGABYTE = 1024L * 1024L;

     public static long bytesToMegabytes(long bytes) {
         return bytes / MEGABYTE;
     }

     public static void main(String[] args) {
         // I assume you will know how to create an object Person yourself...
         List <Person> list = new ArrayList <Person> ();
         for (int i = 0; i <= 100_000; i++) {
             list.add(new Person("Jim", "Knopf"));
         }

         // Get the Java runtime
         Runtime runtime = Runtime.getRuntime();

         // Run the garbage collector
         runtime.gc();

         // Calculate the used memory
         long memory = runtime.totalMemory() - runtime.freeMemory();
         System.out.println("Used memory is bytes: " + memory);
         System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory));
     }
 }
于 2013-01-24T12:55:32.727 に答える
7

100 個の属性を持つ 1 つのオブジェクトが消費するメモリ スペースは、それぞれ 1 つの属性を持つ 100 個のオブジェクトの場合と同じですか?

いいえ。

オブジェクトに割り当てられるメモリの量は?

  • オーバーヘッドは、32 ビットでは 8 バイト、64 ビットでは 12 バイトです。4 バイト (32 ビット) または 8 バイト (64 ビット) の倍数に切り上げられます。

属性を追加するときに使用される追加のスペースはどれくらいですか?

  • 属性の範囲は 1 バイト (byte) から 8 バイト (long/double) ですが、参照は 32 ビットか 64 ビットかに応じて 4 バイトまたは 8 バイトのいずれかになりますが、-Xmx が < 32Gb または >= 32Gb の場合: 標準 64 -bit JVM には「-UseCompressedOops」と呼ばれる最適化機能があり、ヒープが 32Gb 未満の場合に参照を 4 バイトに圧縮します。
于 2016-02-15T11:16:15.507 に答える
5

いいえ、オブジェクトの登録にも少しメモリが必要です。1 つの属性を持つ 100 個のオブジェクトは、より多くのメモリを消費します。

于 2008-11-03T08:26:17.720 に答える
3

別の回答で言及されているjava.lang.instrument.Instrumentationアプローチから非常に良い結果が得られました。その使用例については、JavaSpecialists' Newsletter のInstrumentation Memory Counterのエントリと、SourceForge のjava.sizeOfライブラリを参照してください。

于 2008-11-04T01:27:30.220 に答える
3

誰にとっても役立つ場合は、オブジェクトのメモリ使用量を照会するための小さな Java エージェントを私の Web サイトからダウンロードできます。「深い」メモリ使用量も照会できます。

于 2009-04-07T17:10:51.767 に答える
1

いいえ、100 個の小さなオブジェクトは、1 つの大きなオブジェクトよりも多くの情報 (メモリ) を必要とします。

于 2008-11-03T08:27:12.697 に答える
0

メモリの消費量に関するルールは、JVM の実装と CPU アーキテクチャ (32 ビットと 64 ビットなど) によって異なります。

SUN JVM の詳細なルールについては、私の古いブログを確認してください

よろしく、 マーカス

于 2008-11-13T08:18:12.680 に答える