Javaでオブジェクトのサイズを取得する方法

1概要

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

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

2. Javaにおけるメモリ消費

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

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

ハードウェアとOSキャッシュの影響(私たちのデータはすべてのキャッシュレベルで複製される可能性があります)も考慮に入れると、それは RAMの消費をおおまかに予測することしかできません

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

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

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

つまり、64ビットJVMでは通常30〜50%多くのヒープスペースが必要です。

特に重要なのは、ボックス型、配列、 __String、そして多次元配列のような他のコンテナは、いくらかのオーバーヘッドを追加するため、メモリコストがかかることです。たとえば、 int プリミティブ(4バイトしか消費しない)と16バイトの Integer__オブジェクトを比較すると、300%のメモリオーバーヘッドがあることがわかります。

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

Javaでオブジェクトのサイズを見積もるには、https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/Instrumentation.htmlの getObjectSize(Object) メソッドを使用する方法があります。[ Instrumentation interface]はJava 5で導入されました。

Javadocのドキュメントでわかるように、このメソッドは指定されたオブジェクトのサイズの「実装固有の近似値」を提供します。

サイズにオーバーヘッドが含まれる可能性があり、1回のJVM呼び出しで値が異なる可能性があることは注目に値します。

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

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

オブジェクトのサイズを取得するために Instrumentation.getObjectSize(Object) を呼び出すには、最初にInstrumentationのインスタンスにアクセスできる必要があります。 インストルメンテーションエージェントを使用する必要があります 2つの方法があります。https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/packageのドキュメントに記載されています。 -summary.html[ java.lang.instrument パッケージ]

インストルメンテーションエージェントはコマンドラインで指定することも、すでに実行中の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オブジェクトの推定サイズを取得できるかを示しました。

いつものように、この記事に関連する完全なコードはhttps://github.com/eugenp/tutorials/tree/master/core-java/[GitHubプロジェクト]にあります。