web-dev-qa-db-ja.com

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

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

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

202
manuel

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

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

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

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

測定方法

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

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

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

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

通常、32ビットプラットフォームまたは-Xmx32Gまでの64ビットプラットフォームでは、参照は4バイトです。 32Gbを超える8バイト(-Xmx32G)。 圧縮オブジェクト参照 を参照)

その結果、64ビットJVMは通常、30〜50%多いヒープスペースを必要とします。 2ビットまたは64ビットのJVMを使用する必要がありますか? 、2012、JDK 1.7)

ボックス型、配列、および文字列

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

  • Integer:16バイトの結果は、intの値が余分な4バイトだけに収まるため、予想より少し悪くなります。 Integerを使用すると、値をプリミティブ型として保存できる場合と比較して、300%のメモリオーバーヘッドがかかります

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

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

  • 多次元配列:それはまた別の驚きを提供します。
    開発者は通常、数値計算や科学計算で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++の状況と比較してください。

  • StringStringのメモリ増加は、内部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
}

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

169
VonC

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

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>
30

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

public class SingleByte
{
    private byte b;
}

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

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

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

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

27
Jon Skeet

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

番号。

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

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

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

  • 属性の範囲は1バイト(byte)から8バイト(long/double)ですが、参照は4バイトまたは8バイトのいずれかに依存しますnot 32ビットか64ビットかではなく、-Xmxが32 Gb未満かどうかまたは> = 32Gb:典型的な64ビットJVMには、「-UseCompressedOops」と呼ばれる最適化があり、ヒープが32Gb未満の場合に参照を4バイトに圧縮します。
5
Jayen

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

5
Mendelt

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

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

enter image description here

http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-Java-tutorial.pdf も非常に有益です。例えば:

enter image description here

5
Arun

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

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));
     }
 }
4
nazar_art

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

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

また、ヘッダーおよび参照用に追加のメモリ要件がいくつかあります。

Javaオブジェクトで使用されるヒープメモリには以下が含まれます

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

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

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

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

Javaオブジェクトのヘッダーサイズは、32ビットおよび64ビットjvmによって異なります。

これらはメインメモリコンシューマですが、jvmはコードe.t.cの配置などの追加フィールドも必要とします。

プリミティブ型のサイズ

ブール値とバイト-1

char&short-2

int&float-4

long&double-8

3
Nikhil Agrawal

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

3
Matt Passell

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

2
Neil Coffey

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

1
Burkhard

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

Sun JVMチェックの詳細なルールについては、 my old blog

よろしく、 Markus

0
kohlerm