Javaでのオブジェクトのメモリ消費はどのくらいですか?


216

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

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

回答:


180

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

プリミティブは公式のサイズであるかのように動作する必要がありますが、JVMは、内部またはビッグエンディアンまたはリトルエンディアンの任意の方法でデータを自由に格納できます。
たとえば、JVMまたはネイティブコンパイラは、boolean[]を64ビットの長いチャンクにのように格納することを決定する場合がありBitSetます。プログラムが同じ答えを出す限り、それはあなたに言う必要はありません。

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

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

測定方法

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

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

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

最新の64ビットJDKでは、オブジェクトには12バイトのヘッダーがあり、8バイトの倍数にパディングされているため、オブジェクトの最小サイズは16バイトです。32ビットJVMの場合、オーバーヘッドは8バイトで、4バイトの倍数になるように埋め込まれます。 (からドミトリー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バイトのワード境界に強制的に配置するためです。

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

  • 多次元配列:もう1つの驚きがあります。
    開発者は通常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:a Stringのメモリの増加は、内部のchar配列の増加を追跡します。ただし、Stringクラスはさらに24バイトのオーバーヘッドを追加します。

    Stringサイズが10文字以下の空でない場合、有効なペイロード(各文字に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バイトが割り当てられます。


int [128] [6]:128個の6 int-合計768 intの配列、3072バイトのデータ+ 2064バイトオブジェクトのオーバーヘッド=合計5166バイト。int [256]:合計256整数-比較不可能。int [768]:3072バイトのデータ+ 16バイトのオーバーヘッド-2Dアレイの約3/5のスペース-246%のオーバーヘッドではありません!
JeeBee 2009

ああ、元の記事ではint [128] [2]ではなくint [128] [2]を使用していましたが、どのように変更されたのでしょうか。極端な例が別の話をすることができることも示しています。
JeeBee

2
オーバーヘッドは、64ビットJVMでは16バイトです。
Tim Cooper、

3
@AlexWien:一部のガベージコレクション方式では、パディングとは別の最小オブジェクトサイズが課される場合があります。ガベージコレクション中に、オブジェクトが古い場所から新しい場所にコピーされると、古い場所はオブジェクトのデータを保持する必要がなくなりますが、新しい場所への参照を保持する必要があります。また、最初の参照が発見されたオブジェクトの古い場所への参照と、古いオブジェクト内のその参照のオフセットを保存する必要がある場合もあります(古いオブジェクトにはまだ処理されていない参照がまだ含まれている場合があるため)。
スーパーキャット'19

2
@AlexWien:ガベージコレクターの簿記情報を保持するためにオブジェクトの古い場所でメモリを使用すると、その目的のために他のメモリを割り当てる必要がなくなりますが、他の方法で必要とされるよりも大きい最小オブジェクトサイズが課される場合があります。少なくとも1つのバージョンの.NETガベージコレクターがそのアプローチを使用していると思います。もちろん、一部のJavaガベージコレクターでも同じことが可能です。
スーパーキャット'19

34

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

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の場合、インスタンスサイズは16バイトです。これは、4バイトのintがヘッダーの直後とパディング境界の前の場所で圧縮されるためです。

コードサンプル:

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>

28

各オブジェクトには、関連するモニターとタイプ情報、およびフィールド自体に特定のオーバーヘッドがあります。それ以外では、フィールドはかなりレイアウトできますが、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によって確かに異なる可能性があります。1つの実装ではフィールドをにパックしないことを決定する場合がありOneHundredBytes、408バイト(= 8バイトのオーバーヘッド+ 4 * 100アライン/パディングバイト)が必要になります。64ビットJVMでは、オーバーヘッドも大きくなる可能性があります(不明)。

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

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


9
実際には、シングルバイトの1つのインスタンスは、8の倍数にHotSpotのコンパイラのラウンドのすべて以来、フィールドの8バイトのオーバーヘッド、4バイトである日JVM、上の16バイト、およびオブジェクトのパディングのためのその後、4バイトを取るだろう
ポールWagland

6

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

java.lang.Runtime.getRuntime();

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

package test;

 import java.util.ArrayList;
 import java.util.List;

 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 a object Person yourself...
         List < Person > list = new ArrayList < Person > ();
         for (int i = 0; i <= 100000; 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));
     }
 }

6

すべてのオブジェクトには、32ビットシステムでは16バイト(64ビットシステムでは24バイト)のオーバーヘッドがあるようです。

http://algs4.cs.princeton.edu/14analysis/は、優れた情報源です。多くの良い例の1つは次のとおりです。

ここに画像の説明を入力してください

http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdfも参考になります。次に例を示します。

ここに画像の説明を入力してください


「すべてのオブジェクトには、32ビットシステムでは16バイト(64ビットシステムでは24バイト)のオーバーヘッドがあるようです。」少なくとも現在のJDKでは、これは正しくありません。整数の例に対する私の答えを見てみましょう。オブジェクトのオーバーヘッドは、64ビットシステムおよび最新のJDKのヘッダー用に少なくとも12バイトです。パディングのために多くなる可能性があり、オブジェクトのフィールドの実際のレイアウトに依存します。
Dmitry Spikhalskiy 2015

メモリ効率の高いJavaチュートリアルへの2番目のリンクは機能していないようです。「禁止」されます。
tsleyson 2017

6

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

番号。

オブジェクトにどのくらいのメモリが割り当てられていますか?

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

属性を追加するとき、どのくらいの追加のスペースが使用されますか?

  • 属性は1バイト(バイト)から8バイト(ロング/二重)までの範囲が、参照はいずれかによって4バイトまたは8つのバイトではありません -Xmxであるそれの32ビットまたは64ビットか、むしろかどう<32GBの、または> = 32GBのに典型的な64 -bit JVMには、「-UseCompressedOops」と呼ばれる最適化があり、ヒープが32Gb未満の場合、参照を4バイトに圧縮します。

1
charは8ビットではなく16ビットです。
コモナード

その通りその通り。誰かが私の元の回答を編集したようです
Jayen

5

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


4

質問は非常に幅広いものになります。

それはクラス変数に依存するか、Javaでメモリ使用量を状態として呼び出すことができます。

また、ヘッダーと参照のための追加のメモリ要件もあります。

Javaオブジェクトが使用するヒープメモリには、

  • サイズに応じたプリミティブフィールドのメモリ(プリミティブ型のサイズについては以下を参照)

  • 参照フィールド用のメモリ(各4バイト);

  • 数バイトの「ハウスキーピング」情報で構成されるオブジェクトヘッダー。

Javaのオブジェクトには、オブジェクトのクラス、ID、オブジェクトに現在到達可能かどうか、現在同期ロックされているかどうかなどのステータスフラグを記録するなど、いくつかの「ハウスキーピング」情報も必要です。

Javaオブジェクトのヘッダーサイズは、32ビットと64ビットのJVMで異なります。

これらはメインメモリのコンシューマーですが、jvmにはコードの配置などの追加フィールドが必要になる場合もあります。

プリミティブ型のサイズ

ブール&バイト -1

char&short -2

int&float -4

ロング&ダブル -8


読者も、この論文は非常に照明を見つけることがあります。cs.virginia.edu/kim/publicity/pldi09tutorials/...
quellish



1

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


0

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

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

よろしく、 Markus


Sun Java 1.6 64ビット、プレーンオブジェクト+ 4パディング= 16には12バイトが必要だと確信しています。オブジェクト+ 1つの整数フィールド= 12 + 4 = 16
AlexWien

ブログをシャットダウンしましたか?
JohanBoulé

確かに、SAPブログがどうにか移動したかどうかは不明です。そのほとんどはここkohlerm.blogspot.com
kohlerm
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.