Javaでオブジェクトのサイズを取得する方法
1. 概要
sizeof()メソッドを使用してオブジェクトサイズをバイト単位で取得できるC / C ++とは異なり、Javaにはそのようなメソッドに相当するものはありません。
この記事では、特定のオブジェクトのサイズを取得する方法を示します。
2. Javaのメモリ消費
Javaにはsizeof演算子はありませんが、実際には必要ありません。 すべてのプリミティブ型には標準サイズがあり、通常、パッドまたはアライメントバイトはありません。 それでも、これは必ずしも簡単ではありません。
Although primitives must behave as if they have the official sizes, a JVM can store data in any way it pleases internally, with any amount of padding or overhead。 boolean[]をBitSetのような64ビット長のチャンクに格納するか、スタックに一時的なObjectを割り当てるか、変数またはメソッド呼び出しを最適化して、それらを定数に置き換えるかを選択できます。 、など…しかし、プログラムが同じ結果をもたらす限り、それはまったく問題ありません。
ハードウェアとOSのキャッシュの影響(データはすべてのキャッシュレベルで複製される可能性があります)も考慮すると、we can only roughly predict RAM consumptionを意味します。
2.1. オブジェクト、参照、ラッパークラス
オブジェクトには12バイトのヘッダーがあり、8バイトの倍数に埋め込まれているため、Minimum object size is 16 bytes for modern 64-bit JDK。 32ビットJDKでは、オーバーヘッドは8バイトで、4バイトの倍数にパディングされます。
ヒープ境界が32Gb(-Xmx32G)未満のReferences have a typical size of 4 bytes on 32-bit platforms and on 64-bits platforms、および32Gbを超えるこの境界の場合は8バイト。
つまり、64ビットJVMには通常、30〜50%のヒープスペースが必要です。
特に重要なのは、boxed types, arrays, Strings and other containers like multidimensional arrays are memory costly since they add certain overheadに注意することです。 たとえば、intプリミティブ(4バイトのみを消費する)を16バイトを使用するIntegerオブジェクトと比較すると、300%のメモリオーバーヘッドがあることがわかります。
3. インストルメンテーションを使用したオブジェクトサイズの推定
Javaでオブジェクトのサイズを見積もる1つの方法は、Java 5で導入されたInstrumentation interfaceのgetObjectSize(Object)メソッドを使用することです。
Javadocのドキュメントに示されているように、このメソッドは、指定されたオブジェクトのサイズの「実装固有の近似」を提供します。 サイズにオーバーヘッドが含まれる可能性があり、単一のJVM呼び出し中に値が異なる可能性があることは注目に値します。
This approach only supports size estimation of the considered object itself and not the sizes of objects it references。 オブジェクトの合計サイズを推定するには、これらの参照を調べて推定サイズを計算するコードが必要です。
3.1. インストルメンテーションエージェントの作成
Instrumentation.getObjectSize(Object)を呼び出してオブジェクトのサイズを取得するには、最初にInstrumentationのインスタンスにアクセスできる必要があります。 We need to use the instrumentation agentと、java.lang.instrument package.のドキュメントで説明されているように、これを行うには2つの方法があります。
Instrumentation agent can be specified via the command-line or we can use it with an already running JVM。 最初のものに焦点を当てます。
specify the instrumentation agent via the command-lineには、インストルメンテーションを使用するときにJVMによって最初に呼び出されるオーバーロードされたpremainメソッドの実装が必要です。 さらに、Instrumentation.getObjectSize(Object)にアクセスできるように、静的メソッドを公開する必要があります。
InstrumentationAgentクラスを作成しましょう:
public class InstrumentationAgent {
private static volatile Instrumentation globalInstrumentation;
public static void premain(final String agentArgs, final Instrumentation inst) {
globalInstrumentation = inst;
}
public static long getObjectSize(final Object object) {
if (globalInstrumentation == null) {
throw new IllegalStateException("Agent not initialized.");
}
return globalInstrumentation.getObjectSize(object);
}
}
Before we create a JAR for this agent, we need to make sure that a simple metafile, MANIFEST.MF is included in it:
Premain-class: com.example.objectsize.InstrumentationAgent
これで、MANIFEST.MFファイルが含まれたエージェントJARを作成できます。 1つの方法は、コマンドラインを使用することです:
javac InstrumentationAgent.java
jar cmf MANIFEST.MF InstrumentationAgent.jar InstrumentationAgent.class
3.2. サンプルクラス
エージェントクラスを利用するサンプルオブジェクトを使用してクラスを作成することにより、これが実際に動作することを確認しましょう。
public class InstrumentationExample {
public static void printObjectSize(Object object) {
System.out.println("Object type: " + object.getClass() +
", size: " + InstrumentationAgent.getObjectSize(object) + " bytes");
}
public static void main(String[] arguments) {
String emptyString = "";
String string = "Estimating Object Size Using Instrumentation";
String[] stringArray = { emptyString, string, "com.example" };
String[] anotherStringArray = new String[100];
List stringList = new ArrayList<>();
StringBuilder stringBuilder = new StringBuilder(100);
int maxIntPrimitive = Integer.MAX_VALUE;
int minIntPrimitive = Integer.MIN_VALUE;
Integer maxInteger = Integer.MAX_VALUE;
Integer minInteger = Integer.MIN_VALUE;
long zeroLong = 0L;
double zeroDouble = 0.0;
boolean falseBoolean = false;
Object object = new Object();
class EmptyClass {
}
EmptyClass emptyClass = new EmptyClass();
class StringClass {
public String s;
}
StringClass stringClass = new StringClass();
printObjectSize(emptyString);
printObjectSize(string);
printObjectSize(stringArray);
printObjectSize(anotherStringArray);
printObjectSize(stringList);
printObjectSize(stringBuilder);
printObjectSize(maxIntPrimitive);
printObjectSize(minIntPrimitive);
printObjectSize(maxInteger);
printObjectSize(minInteger);
printObjectSize(zeroLong);
printObjectSize(zeroDouble);
printObjectSize(falseBoolean);
printObjectSize(Day.TUESDAY);
printObjectSize(object);
printObjectSize(emptyClass);
printObjectSize(stringClass);
}
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
}
これが機能するためには、we need to include –javaagent option with the path to agent JAR when running our application:
VM Options: -javaagent:"path_to_agent_directory\InstrumentationAgent.jar"
クラスを実行すると、推定オブジェクトサイズが表示されます。
Object type: class java.lang.String, size: 24 bytes
Object type: class java.lang.String, size: 24 bytes
Object type: class [Ljava.lang.String;, size: 32 bytes
Object type: class [Ljava.lang.String;, size: 416 bytes
Object type: class java.util.ArrayList, size: 24 bytes
Object type: class java.lang.StringBuilder, size: 24 bytes
Object type: class java.lang.Integer, size: 16 bytes
Object type: class java.lang.Integer, size: 16 bytes
Object type: class java.lang.Integer, size: 16 bytes
Object type: class java.lang.Integer, size: 16 bytes
Object type: class java.lang.Long, size: 24 bytes
Object type: class java.lang.Double, size: 24 bytes
Object type: class java.lang.Boolean, size: 16 bytes
Object type: class com.example.objectsize.InstrumentationExample$Day, size: 24 bytes
Object type: class java.lang.Object, size: 16 bytes
Object type: class com.example.objectsize.InstrumentationExample$1EmptyClass, size: 16 bytes
Object type: class com.example.objectsize.InstrumentationExample$1StringClass, size: 16 bytes
4. Conclusion
この記事では、Javaの特定のタイプがメモリを使用する方法、JVMがデータを保存する方法、および総メモリ消費量に影響を与える可能性のあるものについて説明しました。 次に、実際にJavaオブジェクトの推定サイズを取得する方法を示しました。
いつものように、この記事に関連する完全なコードはGitHub projectにあります。