Java でオブジェクトのサイズを決定する最良の方法は何ですか?

StackOverflow https://stackoverflow.com/questions/52353

  •  09-06-2019
  •  | 
  •  

質問

たとえば、山積みのデータ行を含む CSV ファイルを読み取ることができるアプリケーションがあるとします。データの種類に基づいて行数の概要をユーザーに提供しますが、あまりにも多くの行のデータを読み取って問題が発生しないようにしたいと考えています。 OutOfMemoryErrors.各行はオブジェクトに変換されます。そのオブジェクトのサイズをプログラムで調べる簡単な方法はありますか?プリミティブ型とオブジェクト参照の大きさを定義する参照はありますか? VM?

現在、「まで読んでください」というコードがあります。 32,000行, 、しかし、使い終わるまでできるだけ多くの行を読み取るというコードも欲しいです。 32MB 記憶の。もしかしたら違う質問かもしれませんが、それでも知りたいです。

役に立ちましたか?

解決

使用できます java.lang.instrument パッケージ

このクラスをコンパイルして JAR に入れます。

import java.lang.instrument.Instrumentation;

public class ObjectSizeFetcher {
    private static Instrumentation instrumentation;

    public static void premain(String args, Instrumentation inst) {
        instrumentation = inst;
    }

    public static long getObjectSize(Object o) {
        return instrumentation.getObjectSize(o);
    }
}

以下を追加してください MANIFEST.MF:

Premain-Class: ObjectSizeFetcher

getObjectSize を使用します。

public class C {
    private int x;
    private int y;

    public static void main(String [] args) {
        System.out.println(ObjectSizeFetcher.getObjectSize(new C()));
    }
}

次のコマンドで呼び出します:

java -javaagent:ObjectSizeFetcherAgent.jar C

他のヒント

使用する必要があります ジョル, 、OpenJDK プロジェクトの一部として開発されたツール。

JOL (Java Object Layout) は、JVM のオブジェクト レイアウト スキームを分析するための小さなツールボックスです。これらのツールは、実際のオブジェクトのレイアウト、フットプリント、参照をデコードするために Unsafe、JVMTI、Serviceability Agent (SA) を多用しています。これにより、JOL はヒープ ダンプや仕様の仮定などに依存する他のツールよりもはるかに正確になります。

プリミティブ、参照、配列要素のサイズを取得するには、次を使用します。 VMSupport.vmDetails(). 。64 ビット Windows 上で実行されている Oracle JDK 1.8.0_40 (以下のすべての例で使用) では、このメソッドは戻り値を返します。

Running 64-bit HotSpot VM.
Using compressed oop with 0-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]

次を使用して、オブジェクト インスタンスの浅いサイズを取得できます。 ClassLayout.parseClass(Foo.class).toPrintable() (オプションでインスタンスを渡します) toPrintable)。これは、そのクラスの 1 つのインスタンスによって消費されるスペースにすぎません。そのクラスによって参照される他のオブジェクトは含まれません。それ する これには、オブジェクト ヘッダー、フィールドの配置、パディングの VM オーバーヘッドが含まれます。のために java.util.regex.Pattern:

java.util.regex.Pattern object internals:
 OFFSET  SIZE        TYPE DESCRIPTION                    VALUE
      0     4             (object header)                01 00 00 00 (0000 0001 0000 0000 0000 0000 0000 0000)
      4     4             (object header)                00 00 00 00 (0000 0000 0000 0000 0000 0000 0000 0000)
      8     4             (object header)                cb cf 00 20 (1100 1011 1100 1111 0000 0000 0010 0000)
     12     4         int Pattern.flags                  0
     16     4         int Pattern.capturingGroupCount    1
     20     4         int Pattern.localCount             0
     24     4         int Pattern.cursor                 48
     28     4         int Pattern.patternLength          0
     32     1     boolean Pattern.compiled               true
     33     1     boolean Pattern.hasSupplementary       false
     34     2             (alignment/padding gap)        N/A
     36     4      String Pattern.pattern                (object)
     40     4      String Pattern.normalizedPattern      (object)
     44     4        Node Pattern.root                   (object)
     48     4        Node Pattern.matchRoot              (object)
     52     4       int[] Pattern.buffer                 null
     56     4         Map Pattern.namedGroups            null
     60     4 GroupHead[] Pattern.groupNodes             null
     64     4       int[] Pattern.temp                   null
     68     4             (loss due to the next object alignment)
Instance size: 72 bytes (reported by Instrumentation API)
Space losses: 2 bytes internal + 4 bytes external = 6 bytes total

次を使用すると、オブジェクト インスタンスの詳細なサイズの概要ビューを取得できます。 GraphLayout.parseInstance(obj).toFootprint(). 。もちろん、フットプリント内の一部のオブジェクトは共有されている (他のオブジェクトからも参照されている) 可能性があるため、そのオブジェクトがガベージ コレクションされたときに再利用できるスペースの過大近似になります。の結果については、 Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$") (から抜粋 この答え)、jol は合計 1840 バイトのフットプリントを報告しますが、そのうちパターン インスタンス自体は 72 バイトのみです。

java.util.regex.Pattern instance footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1       112       112   [C
         3       272       816   [Z
         1        24        24   java.lang.String
         1        72        72   java.util.regex.Pattern
         9        24       216   java.util.regex.Pattern$1
        13        24       312   java.util.regex.Pattern$5
         1        16        16   java.util.regex.Pattern$Begin
         3        24        72   java.util.regex.Pattern$BitClass
         3        32        96   java.util.regex.Pattern$Curly
         1        24        24   java.util.regex.Pattern$Dollar
         1        16        16   java.util.regex.Pattern$LastNode
         1        16        16   java.util.regex.Pattern$Node
         2        24        48   java.util.regex.Pattern$Single
        40                1840   (total)

代わりに使用する場合 GraphLayout.parseInstance(obj).toPrintable(), jol は、各参照オブジェクトへのフィールド逆参照のアドレス、サイズ、タイプ、値、パスを教えてくれますが、通常は詳細すぎて役に立ちません。進行中のパターンの例では、次のようになります。(アドレスは実行ごとに変更される可能性があります。)

java.util.regex.Pattern object externals:
          ADDRESS       SIZE TYPE                             PATH                           VALUE
         d5e5f290         16 java.util.regex.Pattern$Node     .root.next.atom.next           (object)
         d5e5f2a0        120 (something else)                 (somewhere else)               (something else)
         d5e5f318         16 java.util.regex.Pattern$LastNode .root.next.next.next.next.next.next.next (object)
         d5e5f328      21664 (something else)                 (somewhere else)               (something else)
         d5e647c8         24 java.lang.String                 .pattern                       (object)
         d5e647e0        112 [C                               .pattern.value                 [^, [, a, -, z, A, -, Z, 0, -, 9, _, ., +, -, ], +, @, [, a, -, z, A, -, Z, 0, -, 9, -, ], +, \, ., [, a, -, z, A, -, Z, 0, -, 9, -, ., ], +, $]
         d5e64850        448 (something else)                 (somewhere else)               (something else)
         d5e64a10         72 java.util.regex.Pattern                                         (object)
         d5e64a58        416 (something else)                 (somewhere else)               (something else)
         d5e64bf8         16 java.util.regex.Pattern$Begin    .root                          (object)
         d5e64c08         24 java.util.regex.Pattern$BitClass .root.next.atom.val$rhs        (object)
         d5e64c20        272 [Z                               .root.next.atom.val$rhs.bits   [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64d30         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d48         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d60         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d78         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d90         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64da8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64dc0         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs (object)
         d5e64dd8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs        (object)
         d5e64df0         24 java.util.regex.Pattern$5        .root.next.atom                (object)
         d5e64e08         32 java.util.regex.Pattern$Curly    .root.next                     (object)
         d5e64e28         24 java.util.regex.Pattern$Single   .root.next.next                (object)
         d5e64e40         24 java.util.regex.Pattern$BitClass .root.next.next.next.atom.val$rhs (object)
         d5e64e58        272 [Z                               .root.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64f68         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64f80         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e64f98         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs.val$lhs (object)
         d5e64fb0         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$rhs (object)
         d5e64fc8         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs (object)
         d5e64fe0         24 java.util.regex.Pattern$5        .root.next.next.next.atom      (object)
         d5e64ff8         32 java.util.regex.Pattern$Curly    .root.next.next.next           (object)
         d5e65018         24 java.util.regex.Pattern$Single   .root.next.next.next.next      (object)
         d5e65030         24 java.util.regex.Pattern$BitClass .root.next.next.next.next.next.atom.val$rhs (object)
         d5e65048        272 [Z                               .root.next.next.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e65158         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e65170         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e65188         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e651a0         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e651b8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs (object)
         d5e651d0         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs (object)
         d5e651e8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom (object)
         d5e65200         32 java.util.regex.Pattern$Curly    .root.next.next.next.next.next (object)
         d5e65220        120 (something else)                 (somewhere else)               (something else)
         d5e65298         24 java.util.regex.Pattern$Dollar   .root.next.next.next.next.next.next (object)

「(何か他のもの)」エントリ このオブジェクト グラフの一部ではないヒープ内の他のオブジェクトを説明します.

最高の jol ドキュメントは、 ジョルサンプル jolリポジトリ内にあります。このサンプルでは、​​一般的な jol 操作を示し、jol を使用して VM とガベージ コレクターの内部を分析する方法を示します。

数年前、Javaworld では 複合 Java オブジェクトおよびネストされている可能性のある Java オブジェクトのサイズの決定に関する記事, では、基本的に Java で sizeof() 実装を作成する手順を説明します。このアプローチは基本的に、プリミティブと典型的な Java オブジェクトのサイズを実験的に特定し、その知識をオブジェクト グラフを再帰的に実行して合計サイズを集計するメソッドに適用するという他の研究に基づいて構築されています。

クラスの舞台裏で何が起こっているのかという理由だけで、ネイティブ C 実装よりも正確さは常にいくらか低くなりますが、良い指標となるはずです。

あるいは、適切に呼ばれる SourceForge プロジェクト のサイズ これは、sizeof() 実装を備えた Java5 ライブラリを提供します。

追伸シリアル化アプローチは使用しないでください。シリアル化されたオブジェクトのサイズと、ライブ時に消費されるメモリ量の間には相関関係がありません。

まず、「オブジェクトのサイズ」は Java では明確に定義された概念ではありません。オブジェクト自体とそのメンバーのみ、オブジェクトとそれが参照するすべてのオブジェクト (参照グラフ) を意味する場合もあります。メモリ内のサイズまたはディスク上のサイズを意味する可能性があります。また、JVM は文字列などを最適化することができます。

したがって、唯一の正しい方法は、優れたプロファイラーを使用して JVM に問い合わせることです (私は あなたのキット)、これはおそらくあなたが望んでいることではありません。

ただし、上記の説明から、各行は自己完結型であり、大きな依存関係ツリーを持たないように聞こえるため、シリアル化方法はおそらくほとんどの JVM で適切な近似値となるでしょう。これを行う最も簡単な方法は次のとおりです。

 Serializable ser;
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(baos);
 oos.writeObject(ser);
 oos.close();
 return baos.size();

共通の参照を持つオブジェクトがある場合は、次のことを覚えておいてください。 しない 正しい結果が得られ、シリアル化のサイズはメモリ内のサイズと必ずしも一致するとは限りませんが、これは適切な近似値です。ByteArrayOutputStream サイズを適切な値に初期化すると、コードはもう少し効率的になります。

誤ってJavaクラス「jdk.nashorn.internal.ir.debug.objectsizecalculator」を見つけました。

System.out.println(ObjectSizeCalculator.getObjectSize(new gnu.trove.map.hash.TObjectIntHashMap<String>(12000, 0.6f, -1)));
System.out.println(ObjectSizeCalculator.getObjectSize(new HashMap<String, Integer>(100000)));
System.out.println(ObjectSizeCalculator.getObjectSize(3));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[]{1, 2, 3, 4, 5, 6, 7 }));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[100]));

結果:

164192
48
16
48
416

JVM で使用されているメモリの量と空きメモリの量を知りたいだけの場合は、次のようなことを試してください。

// Get current size of heap in bytes
long heapSize = Runtime.getRuntime().totalMemory();

// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
// Any attempt will result in an OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();

// Get amount of free memory within the heap in bytes. This size will increase
// after garbage collection and decrease as new objects are created.
long heapFreeSize = Runtime.getRuntime().freeMemory();

編集:質問の作成者は、「32MB のメモリを使用するまで、できるだけ多くの行を読み取る」処理を行うロジックが欲しいとも述べていたので、これは役立つかもしれないと思いました。

Twitter で働いていた頃、ディープ オブジェクト サイズを計算するユーティリティを作成しました。さまざまなメモリ モデル (32 ビット、圧縮 oops、64 ビット)、パディング、サブクラス パディングを考慮し、循環データ構造と配列で正しく動作します。この 1 つの .java ファイルをコンパイルするだけです。外部依存関係はありません。

https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java

他の回答の多くは浅いサイズを提供しています。キーや値を含まない HashMap のサイズですが、これはおそらく希望するものではありません。

Jamm プロジェクトは上記の java.lang.instrumentation パッケージを使用しますが、ツリー内を移動するため、メモリを大量に使用する可能性があります。

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

MemoryMeter を使用するには、「-javaagent:/jamm.jar」で JVM を起動します。

反射を使用してオブジェクトを歩く必要があります。以下のことを行う際は注意してください。

  • オブジェクトを割り当てるだけでも、JVM にある程度のオーバーヘッドが発生します。この量は JVM によって異なるため、この値をパラメータにすることもできます。少なくとも定数(8バイト?)にして、割り当てられたものに適用してください。
  • ただだから byte 理論的には 1 バイトであるということは、メモリに 1 バイトしか必要ないという意味ではありません。
  • オブジェクト参照にはループが存在するため、 HashMap またはそのようなもの object-equals をコンパレータとして使用する 無限ループをなくすために。

@ジョドネル:ソリューションのシンプルさは気に入っていますが、多くのオブジェクトはシリアル化可能ではなく (そのため、例外がスローされます)、フィールドは一時的なものになる可能性があり、オブジェクトは標準メソッドをオーバーライドする可能性があります。

これはツールを使用して測定するか、手作業で推定する必要があり、使用している JVM によって異なります。

オブジェクトごとに一定のオーバーヘッドが発生します。これは JVM 固有ですが、通常は 40 バイトと見積もられます。次に、クラスのメンバーを見なければなりません。オブジェクト参照は、32 ビット (64 ビット) JVM では 4 (8) バイトです。プリミティブ型は次のとおりです。

  • ブール値とバイト:1バイト
  • char と short:2バイト
  • int と float:4バイト
  • 長くて倍:8バイト

配列も同じルールに従います。つまり、これはオブジェクト参照であるため、オブジェクト内の 4 (または 8) バイトを必要とし、その長さに要素のサイズを掛けた値になります。

を呼び出してプログラム的にそれを実行しようとしています Runtime.freeMemory() ガベージ コレクターへの非同期呼び出しなどが原因で、あまり精度が得られません。-Xrunhprof またはその他のツールを使用してヒープをプロファイリングすると、最も正確な結果が得られます。

java.lang.instrument.Instrumentation クラスは Java オブジェクトのサイズを取得する優れた方法を提供しますが、そのためには premain そして、Java エージェントを使用してプログラムを実行します。エージェントを必要としない場合に、ダミーの Jar エージェントをアプリケーションに提供する必要がある場合、これは非常に退屈です。

そこで私は、を使用して代替ソリューションを入手しました Unsafe からのクラス sun.misc. 。したがって、プロセッサ アーキテクチャに従ってオブジェクト ヒープの配置を考慮し、最大フィールド オフセットを計算することで、Java オブジェクトのサイズを測定できます。以下の例では補助クラスを使用しています UtilUnsafe への参照を取得するには sun.misc.Unsafe 物体。

private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS/BYTE;
private static final int MIN_SIZE = 16; 

public static int sizeOf(Class src){
    //
    // Get the instance fields of src class
    // 
    List<Field> instanceFields = new LinkedList<Field>();
    do{
        if(src == Object.class) return MIN_SIZE;
        for (Field f : src.getDeclaredFields()) {
            if((f.getModifiers() & Modifier.STATIC) == 0){
                instanceFields.add(f);
            }
        }
        src = src.getSuperclass();
    }while(instanceFields.isEmpty());
    //
    // Get the field with the maximum offset
    //  
    long maxOffset = 0;
    for (Field f : instanceFields) {
        long offset = UtilUnsafe.UNSAFE.objectFieldOffset(f);
        if(offset > maxOffset) maxOffset = offset; 
    }
    return  (((int)maxOffset/WORD) + 1)*WORD; 
}
class UtilUnsafe {
    public static final sun.misc.Unsafe UNSAFE;

    static {
        Object theUnsafe = null;
        Exception exception = null;
        try {
            Class<?> uc = Class.forName("sun.misc.Unsafe");
            Field f = uc.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            theUnsafe = f.get(uc);
        } catch (Exception e) { exception = e; }
        UNSAFE = (sun.misc.Unsafe) theUnsafe;
        if (UNSAFE == null) throw new Error("Could not obtain access to sun.misc.Unsafe", exception);
    }
    private UtilUnsafe() { }
}

もあります。 メモリ測定器 ツール (以前は Googleコード, 、 今 GitHub)、シンプルで商用に適した環境で公開されています。 Apache 2.0ライセンス, で説明したように、 同様の質問.

これも、メモリのバイト消費量を測定したい場合は Java インタプリタへのコマンドライン引数が必要ですが、それ以外の場合は、少なくとも私が使用したシナリオでは問題なく動作するようです。

これは、リンクされた例のいくつかを使用して、32 ビット、64 ビット、および圧縮 OOP を使用した 64 ビットを処理するために作成したユーティリティです。それは使用しています sun.misc.Unsafe.

それは使用しています Unsafe.addressSize() ネイティブ ポインタのサイズを取得し、 Unsafe.arrayIndexScale( Object[].class ) Java リファレンスのサイズ。

既知のクラスのフィールド オフセットを使用して、オブジェクトの基本サイズを計算します。

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.Stack;
import sun.misc.Unsafe;

/** Usage: 
 * MemoryUtil.sizeOf( object )
 * MemoryUtil.deepSizeOf( object )
 * MemoryUtil.ADDRESS_MODE
 */
public class MemoryUtil
{
    private MemoryUtil()
    {
    }

    public static enum AddressMode
    {
        /** Unknown address mode. Size calculations may be unreliable. */
        UNKNOWN,
        /** 32-bit address mode using 32-bit references. */
        MEM_32BIT,
        /** 64-bit address mode using 64-bit references. */
        MEM_64BIT,
        /** 64-bit address mode using 32-bit compressed references. */
        MEM_64BIT_COMPRESSED_OOPS
    }

    /** The detected runtime address mode. */
    public static final AddressMode ADDRESS_MODE;

    private static final Unsafe UNSAFE;

    private static final long ADDRESS_SIZE; // The size in bytes of a native pointer: 4 for 32 bit, 8 for 64 bit
    private static final long REFERENCE_SIZE; // The size of a Java reference: 4 for 32 bit, 4 for 64 bit compressed oops, 8 for 64 bit
    private static final long OBJECT_BASE_SIZE; // The minimum size of an Object: 8 for 32 bit, 12 for 64 bit compressed oops, 16 for 64 bit
    private static final long OBJECT_ALIGNMENT = 8;

    /** Use the offset of a known field to determine the minimum size of an object. */
    private static final Object HELPER_OBJECT = new Object() { byte b; };


    static
    {
        try
        {
            // Use reflection to get a reference to the 'Unsafe' object.
            Field f = Unsafe.class.getDeclaredField( "theUnsafe" );
            f.setAccessible( true );
            UNSAFE = (Unsafe) f.get( null );

            OBJECT_BASE_SIZE = UNSAFE.objectFieldOffset( HELPER_OBJECT.getClass().getDeclaredField( "b" ) );

            ADDRESS_SIZE = UNSAFE.addressSize();
            REFERENCE_SIZE = UNSAFE.arrayIndexScale( Object[].class );

            if( ADDRESS_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_32BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 8 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT_COMPRESSED_OOPS;
            }
            else
            {
                ADDRESS_MODE = AddressMode.UNKNOWN;
            }
        }
        catch( Exception e )
        {
            throw new Error( e );
        }
    }


    /** Return the size of the object excluding any referenced objects. */
    public static long shallowSizeOf( final Object object )
    {
        Class<?> objectClass = object.getClass();
        if( objectClass.isArray() )
        {
            // Array size is base offset + length * element size
            long size = UNSAFE.arrayBaseOffset( objectClass )
                    + UNSAFE.arrayIndexScale( objectClass ) * Array.getLength( object );
            return padSize( size );
        }
        else
        {
            // Object size is the largest field offset padded out to 8 bytes
            long size = OBJECT_BASE_SIZE;
            do
            {
                for( Field field : objectClass.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 )
                    {
                        long offset = UNSAFE.objectFieldOffset( field );
                        if( offset >= size )
                        {
                            size = offset + 1; // Field size is between 1 and PAD_SIZE bytes. Padding will round up to padding size.
                        }
                    }
                }
                objectClass = objectClass.getSuperclass();
            }
            while( objectClass != null );

            return padSize( size );
        }
    }


    private static final long padSize( final long size )
    {
        return (size + (OBJECT_ALIGNMENT - 1)) & ~(OBJECT_ALIGNMENT - 1);
    }


    /** Return the size of the object including any referenced objects. */
    public static long deepSizeOf( final Object object )
    {
        IdentityHashMap<Object,Object> visited = new IdentityHashMap<Object,Object>();
        Stack<Object> stack = new Stack<Object>();
        if( object != null ) stack.push( object );

        long size = 0;
        while( !stack.isEmpty() )
        {
            size += internalSizeOf( stack.pop(), stack, visited );
        }
        return size;
    }


    private static long internalSizeOf( final Object object, final Stack<Object> stack, final IdentityHashMap<Object,Object> visited )
    {
        // Scan for object references and add to stack
        Class<?> c = object.getClass();
        if( c.isArray() && !c.getComponentType().isPrimitive() )
        {
            // Add unseen array elements to stack
            for( int i = Array.getLength( object ) - 1; i >= 0; i-- )
            {
                Object val = Array.get( object, i );
                if( val != null && visited.put( val, val ) == null )
                {
                    stack.add( val );
                }
            }
        }
        else
        {
            // Add unseen object references to the stack
            for( ; c != null; c = c.getSuperclass() )
            {
                for( Field field : c.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 
                            && !field.getType().isPrimitive() )
                    {
                        field.setAccessible( true );
                        try
                        {
                            Object val = field.get( object );
                            if( val != null && visited.put( val, val ) == null )
                            {
                                stack.add( val );
                            }
                        }
                        catch( IllegalArgumentException e )
                        {
                            throw new RuntimeException( e );
                        }
                        catch( IllegalAccessException e )
                        {
                            throw new RuntimeException( e );
                        }
                    }
                }
            }
        }

        return shallowSizeOf( object );
    }
}

インストルメンテーションなどをいじる必要がなく、オブジェクトのバイト正確なサイズを知る必要がない場合は、次のアプローチを使用できます。

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

do your job here

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

このようにして、使用済みメモリを前後で読み取り、使用済みメモリを取得する直前に GC を呼び出すことで、「ノイズ」をほぼ 0 まで下げることができます。

より信頼性の高い結果を得るには、ジョブを n 回実行し、使用されたメモリを n で割って、1 回の実行に必要なメモリ量を取得できます。さらに、全体をより多くの回数実行して平均を算出することもできます。

それが必要な場合、メソッド呼び出しはありません。少し調べれば、自分で書くことができると思います。特定のインスタンスには、参照とプリミティブ値の数に加えてインスタンスの簿記データから導出された固定サイズがあります。オブジェクト グラフをただ歩くだけです。行の種類の変化が少なければ少ないほど、簡単になります。

それが遅すぎる場合、または価値以上に面倒な場合は、古き良き行カウントの経験則が常にあります。

一度、その場で推定するための簡単なテストを書きました。

public class Test1 {

    // non-static nested
    class Nested { }

    // static nested
    static class StaticNested { }

    static long getFreeMemory () {
        // waits for free memory measurement to stabilize
        long init = Runtime.getRuntime().freeMemory(), init2;
        int count = 0;
        do {
            System.out.println("waiting..." + init);
            System.gc();
            try { Thread.sleep(250); } catch (Exception x) { }
            init2 = init;
            init = Runtime.getRuntime().freeMemory();
            if (init == init2) ++ count; else count = 0;
        } while (count < 5);
        System.out.println("ok..." + init);
        return init;
    }

    Test1 () throws InterruptedException {

        Object[] s = new Object[10000];
        Object[] n = new Object[10000];
        Object[] t = new Object[10000];

        long init = getFreeMemory();

        //for (int j = 0; j < 10000; ++ j)
        //    s[j] = new Separate();

        long afters = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            n[j] = new Nested();

        long aftersn = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            t[j] = new StaticNested();

        long aftersnt = getFreeMemory();

        System.out.println("separate:      " + -(afters - init) + " each=" + -(afters - init) / 10000);
        System.out.println("nested:        " + -(aftersn - afters) + " each=" + -(aftersn - afters) / 10000);
        System.out.println("static nested: " + -(aftersnt - aftersn) + " each=" + -(aftersnt - aftersn) / 10000);

    }

    public static void main (String[] args) throws InterruptedException {
        new Test1();
    }

}

一般的な概念は、オブジェクトを割り当て、空きヒープ領域の変化を測定することです。鍵となるのは getFreeMemory(), 、 どれの GC の実行を要求し、報告された空きヒープ サイズが安定するまで待機します。. 。上記の出力は次のとおりです。

nested:        160000 each=16
static nested: 160000 each=16

これは、位置合わせの動作とヒープ ブロック ヘッダーのオーバーヘッドの可能性を考慮すると、予想どおりです。

ここで受け入れられた回答で詳しく説明されている計測方法が最も正確です。私が説明した方法は正確ですが、他のスレッドがオブジェクトを作成/破棄していない制御された条件下でのみ行われます。

Java ビジュアル VM を使用するだけです。

メモリの問題のプロファイリングとデバッグに必要なものがすべて揃っています。

また、OQL (オブジェクト クエリ言語) コンソールも備えており、多くの便利な操作を行うことができます。 sizeof(o)

私の答えは、Nick によって提供されたコードに基づいています。このコードは、シリアル化されたオブジェクトによって占有されるバイトの総量を測定します。したがって、これは実際にシリアル化要素 + プレーンオブジェクトのメモリフットプリントを測定します(たとえばシリアル化するだけです) int シリアル化されたバイトの合計量がそうではないことがわかります。 4)。したがって、オブジェクトに正確に使用される生のバイト番号を取得したい場合は、そのコードを少し変更する必要があります。そのようです:

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ObjectSizeCalculator {
    private Object getFirstObjectReference(Object o) {
        String objectType = o.getClass().getTypeName();

        if (objectType.substring(objectType.length()-2).equals("[]")) {
            try {
                if (objectType.equals("java.lang.Object[]"))
                    return ((Object[])o)[0];
                else if (objectType.equals("int[]"))
                    return ((int[])o)[0];
                else
                    throw new RuntimeException("Not Implemented !");
            } catch (IndexOutOfBoundsException e) {
                return null;
            }
        }

        return o;
    } 

    public int getObjectSizeInBytes(Object o) {
        final String STRING_JAVA_TYPE_NAME = "java.lang.String";

        if (o == null)
            return 0;

        String objectType = o.getClass().getTypeName();
        boolean isArray = objectType.substring(objectType.length()-2).equals("[]");

        Object objRef = getFirstObjectReference(o);
        if (objRef != null && !(objRef instanceof Serializable))
            throw new RuntimeException("Object must be serializable for measuring it's memory footprint using this method !");

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(o);
            oos.close();
            byte[] bytes = baos.toByteArray();

            for (int i = bytes.length - 1, j = 0; i != 0; i--, j++) {
                if (objectType != STRING_JAVA_TYPE_NAME) {
                    if (bytes[i] == 112)
                        if (isArray)
                            return j - 4;
                        else
                            return j;
                } else {
                    if (bytes[i] == 0)
                        return j - 1;
                }
            }
        } catch (Exception e) {
            return -1;
        }

        return -1;
    }    

}

私はこのソリューションをプリミティブ型、String、およびいくつかの簡単なクラスでテストしました。対象外のケースもございます。


アップデート: 配列オブジェクトのメモリ フットプリント計算をサポートするために例が変更されました。

(jmap などを使用して) ヒープ ダンプを生成し、その出力を分析してオブジェクト サイズを見つけることができます。これはオフラインのソリューションですが、浅いサイズと深いサイズなどを調べることができます。

long heapSizeBefore = Runtime.getRuntime().totalMemory();

// Code for object construction
...
long heapSizeAfter = Runtime.getRuntime().totalMemory();
long size = heapSizeAfter - heapSizeBefore;

size は、オブジェクトの作成による jvm のメモリ使用量の増加を示します。これは通常、オブジェクトのサイズです。

この答えはオブジェクトのサイズとは関係ありませんが、オブジェクトを収容するために配列を使用している場合です。オブジェクトに割り当てるメモリ サイズ。

したがって、配列、リスト、またはマップのすべてのコレクションは実際にはオブジェクトを保存せず (プリミティブのときのみ、実際のオブジェクトのメモリ サイズが必要です)、それらのオブジェクトの参照のみを保存します。

今、 Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8 バイト) (32/64 ビット) OS に依存

プリミティブ

int   [] intArray    = new int   [1]; will require 4 bytes.
long  [] longArray   = new long  [1]; will require 8 bytes.

オブジェクト

Object[] objectArray = new Object[1]; will require 4 bytes. The object can be any user defined Object.
Long  [] longArray   = new Long  [1]; will require 4 bytes.

つまり、すべてのオブジェクト REFERENCE には 4 バイトのメモリしか必要ないということです。String 参照または Double オブジェクト参照の場合がありますが、オブジェクトの作成によって必要なメモリは異なります。

例)以下のクラスのオブジェクトを作成した場合 ReferenceMemoryTest 4 + 4 + 4 = 12 バイトのメモリが作成されます。参照を初期化しようとすると、メモリが異なる場合があります。

 class ReferenceMemoryTest {
    public String refStr;
    public Object refObj;
    public Double refDoub; 
}

そのため、オブジェクト/参照配列を作成すると、その内容はすべて NULL 参照で占有されます。そして、各参照には 4 バイトが必要であることがわかります。

最後に、以下のコードのメモリ割り当ては 20 バイトです。

ReferenceMemoryTest ref1 = 新しい ReferenceMemoryTest();(4(ref1) + 12 = 16バイト)参照ememorytest ref2 = ref1;( 4(ref2) + 16 = 20 バイト)

という名前のクラスを宣言するとします。 Complex のように:

public class Complex {

    private final long real;
    private final long imaginary;

    // omitted
}

このクラスのライブ インスタンスに割り当てられているメモリの量を確認するには、次のようにします。

$ jmap -histo:live <pid> | grep Complex

 num     #instances         #bytes  class name (module)
-------------------------------------------------------
 327:             1             32  Complex

JSONObject の場合は、以下のコードが役に立ちます。

`JSONObject.toString().getBytes("UTF-8").length`

サイズをバイト単位で返します

JSONArray オブジェクトをファイルに書き込んで確認しました。オブジェクトのサイズを与えています。

一度だけ実行して将来の使用のために保存したい場合を除いて、プログラムで実行する必要があるとは思えません。費用がかかるものです。Java には sizeof() 演算子はありません。あったとしても、他のオブジェクトへの参照のコストとプリミティブのサイズのみがカウントされます。

これを行う 1 つの方法は、次のように、オブジェクトを File にシリアル化し、ファイルのサイズを確認することです。

Serializable myObject;
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser"));
oos.write (myObject);
oos.close ();

もちろん、これは各オブジェクトが個別であり、他のオブジェクトへの非一時的な参照が含まれていないことを前提としています。

もう 1 つの戦略は、各オブジェクトを取得し、リフレクションによってそのメンバーを調べ、サイズ (ブール値とバイト = 1 バイト、ショートと文字 = 2 バイトなど) を合計して、メンバーシップ階層を下位に進むことです。しかし、それは面倒でコストがかかり、結局はシリアル化戦略と同じことをすることになります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top