1. 概要

sizeof ()メソッドを使用してオブジェクトサイズをバイト単位で取得できるC / C ++とは異なり、Javaにはそのようなメソッドに相当するものはありません。

この記事では、特定のオブジェクトのサイズを取得する方法を示します。

2. Javaでのメモリ消費

Javaにはsizeof演算子はありませんが、実際には必要ありません。 すべてのプリミティブタイプには標準サイズがあり、通常、パッドバイトまたはアライメントバイトはありません。 それでも、これは必ずしも簡単ではありません。

プリミティブは公式のサイズであるかのように動作する必要がありますが、JVMは、任意の量のパディングまたはオーバーヘッドを使用して、内部で任意の方法でデータを格納できます boolean []BitSet のような64ビット長のチャンクに格納するか、スタックに一時的な Object を割り当てるか、いくつかの変数またはメソッドを最適化するかを選択できます。呼び出しは完全に存在しなくなり、定数などに置き換えられます…しかし、プログラムが同じ結果をもたらす限り、それは完全に問題ありません。

ハードウェアとOSのキャッシュの影響(データはすべてのキャッシュレベルで複製される可能性があります)も考慮すると、RAM消費量のみを大まかに予測できることを意味します。

2.1. オブジェクト、参照、ラッパークラス

オブジェクトには12バイトのヘッダーがあり、8バイトの倍数に埋め込まれているため、最新の64ビットJDKの最小オブジェクトサイズは16バイトです。 32ビットJDKでは、オーバーヘッドは8バイトで、4バイトの倍数にパディングされます。

参照の標準サイズは、32ビットプラットフォームおよび64ビットプラットフォームで、ヒープ境界が32Gb未満( -Xmx32G )で、この境界が32Gbを超える場合は8バイトです。 。

これは、64ビットJVMは通常30〜50%多くのヒープスペースを必要とすることを意味します。

特に関連するのは、ボックス型、配列、文字列、および多次元配列などの他のコンテナーは、特定のオーバーヘッドを追加するため、メモリコストがかかることに注意することです。 たとえば、 int プリミティブ(4バイトのみを消費)を16バイトを使用する Integer オブジェクトと比較すると、300%のメモリオーバーヘッドがあることがわかります。

3. インストルメンテーションを使用したオブジェクトサイズの推定

Javaでオブジェクトのサイズの見積もりを取得する1つの方法は、Java5で導入されたInstrumentation interfacegetObjectSize(Object)メソッドを使用することです。

Javadocのドキュメントでわかるように、このメソッドは、指定されたオブジェクトのサイズの「実装固有の近似」を提供します。 サイズにオーバーヘッドが含まれる可能性があり、単一のJVM呼び出し中に値が異なる可能性があることに注意してください。

このアプローチは、考慮されるオブジェクト自体のサイズ推定のみをサポートし、参照するオブジェクトのサイズはサポートしません。 オブジェクトの合計サイズを見積もるには、それらの参照を調べて見積サイズを計算するコードが必要になります。

3.1. インストルメンテーションエージェントの作成

Instrumentation.getObjectSize(Object)を呼び出してオブジェクトのサイズを取得するには、最初にInstrumentationのインスタンスにアクセスできる必要があります。 インストルメンテーションエージェントを使用する必要があります。これを行うには、java.lang.instrumentパッケージのドキュメントで説明されているように2つの方法があります。

インストルメンテーションエージェントは、コマンドラインから指定することも、すでに実行中のJVMで使用することもできます。 最初のものに焦点を当てます。

コマンドラインを介してインストルメンテーションエージェントを指定するには、インストルメンテーションを使用するときに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);
    }
}

このエージェントのJARを作成する前に、単純なメタファイルMANIFEST.MFがそれに含まれていることを確認する必要があります

Premain-class: com.baeldung.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.baeldung" };
        String[] anotherStringArray = new String[100];
        List<String> 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
    }
}

これを機能させるには、アプリケーションの実行時にエージェントJARへのパスに–javaagentオプションを含める必要があります

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.baeldung.objectsize.InstrumentationExample$Day, size: 24 bytes
Object type: class java.lang.Object, size: 16 bytes
Object type: class com.baeldung.objectsize.InstrumentationExample$1EmptyClass, size: 16 bytes
Object type: class com.baeldung.objectsize.InstrumentationExample$1StringClass, size: 16 bytes

4. 結論

この記事では、Javaの特定のタイプでメモリがどのように使用されるか、JVMがデータを格納する方法、および総メモリ消費量に影響を与える可能性のあるものを強調しました。 次に、実際にJavaオブジェクトの推定サイズを取得する方法を示しました。

いつものように、この記事に関連する完全なコードはGitHubプロジェクトにあります。