回答:
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実装によって行われる低レベルのメモリアラインメントの影響を受けます。これは、Long
8バイトのオブジェクトオーバーヘッドに加えて、実際の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
:aString
のメモリの増加は、内部の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
}
単純な合計は、のインスタンスがX
17バイトを使用することを示唆しています。ただし、アラインメント(パディングとも呼ばれる)により、JVMはメモリを8バイトの倍数で割り当てるため、17バイトではなく24バイトが割り当てられます。
それは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>
各オブジェクトには、関連するモニターとタイプ情報、およびフィールド自体に特定のオーバーヘッドがあります。それ以外では、フィールドはかなりレイアウトできますが、JVMは適切だと思います(私は信じています)。しかし、別の回答に示されているように、少なくとも一部の JVMはかなり密にパックされます。このようなクラスを考えてみましょう:
public class SingleByte
{
private byte b;
}
対
public class OneHundredBytes
{
private byte b00, b01, ..., b99;
}
32ビットJVMでは、の100インスタンスがSingleByte
1200バイト(パディング/アラインメントのために8バイトのオーバーヘッド+フィールドの4バイト)を使用すると予想します。の1つのインスタンスがOneHundredBytes
108バイト(オーバーヘッド、次に100バイト)をパックすると予想します。ただし、JVMによって確かに異なる可能性があります。1つの実装ではフィールドをにパックしないことを決定する場合がありOneHundredBytes
、408バイト(= 8バイトのオーバーヘッド+ 4 * 100アライン/パディングバイト)が必要になります。64ビットJVMでは、オーバーヘッドも大きくなる可能性があります(不明)。
編集:以下のコメントを参照してください。どうやらHotSpotは32ではなく8バイトの境界にパディングするため、の各インスタンスはSingleByte
16バイトかかります。
どちらの方法でも、「単一の大きなオブジェクト」は、このような単純なケースでは、少なくとも複数の小さなオブジェクトと同じくらい効率的です。
プログラムの合計使用/空きメモリは、プログラムで取得できます
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));
}
}
すべてのオブジェクトには、32ビットシステムでは16バイト(64ビットシステムでは24バイト)のオーバーヘッドがあるようです。
http://algs4.cs.princeton.edu/14analysis/は、優れた情報源です。多くの良い例の1つは次のとおりです。
http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdfも参考になります。次に例を示します。
100の属性を持つ1つのオブジェクトが消費するメモリ領域は、それぞれ1つの属性を持つ100のオブジェクトと同じですか?
番号。
オブジェクトにどのくらいのメモリが割り当てられていますか?
属性を追加するとき、どのくらいの追加のスペースが使用されますか?
質問は非常に幅広いものになります。
それはクラス変数に依存するか、Javaでメモリ使用量を状態として呼び出すことができます。
また、ヘッダーと参照のための追加のメモリ要件もあります。
Javaオブジェクトが使用するヒープメモリには、
サイズに応じたプリミティブフィールドのメモリ(プリミティブ型のサイズについては以下を参照)
参照フィールド用のメモリ(各4バイト);
数バイトの「ハウスキーピング」情報で構成されるオブジェクトヘッダー。
Javaのオブジェクトには、オブジェクトのクラス、ID、オブジェクトに現在到達可能かどうか、現在同期ロックされているかどうかなどのステータスフラグを記録するなど、いくつかの「ハウスキーピング」情報も必要です。
Javaオブジェクトのヘッダーサイズは、32ビットと64ビットのJVMで異なります。
これらはメインメモリのコンシューマーですが、jvmにはコードの配置などの追加フィールドが必要になる場合もあります。
プリミティブ型のサイズ
ブール&バイト -1
char&short -2
int&float -4
ロング&ダブル -8
別の回答で言及されているjava.lang.instrument.Instrumentationアプローチから非常に良い結果を得ました。その使用例については、JavaSpecialists 'NewsletterのInstrumentation Memory CounterとSourceForgeのjava.sizeOfライブラリをご覧ください。
誰かに役立つ場合は、私のWebサイトから、オブジェクトのメモリ使用量を照会するための小さなJavaエージェントをダウンロードできます。「深い」メモリ使用量をクエリすることもできます。
(String, Integer)
Guava Cacheが要素ごとに使用するメモリの概算を取得するのに役立ちました。ありがとう!
消費されるメモリの量に関するルールは、JVMの実装とCPUアーキテクチャ(たとえば、32ビットと64ビット)によって異なります。
SUN JVMの詳細なルールについては、私の古いブログを確認してください
よろしく、 Markus