656

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

現在、 read up to 32,000 rowsというコードがありますが、 32MBのメモリを使用するまで、できるだけ多くの行を読み取るというコードも必要です。質問とは違うかもしれませんが、教えていただきたいです。

4

27 に答える 27

481

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
于 2008-09-09T19:24:37.863 に答える
145

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

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 バイトのみが Pattern インスタンス自体です。

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)

「(something else)」エントリは、このオブジェクト グラフの一部ではないヒープ内の他のオブジェクトを表します

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

于 2015-05-04T00:46:52.350 に答える
109

私は誤って Java クラス "jdk.nashorn.internal.ir.debug.ObjectSizeCalculator" を見つけました。これは既に jdk に含まれています。これは使いやすく、オブジェクトのサイズを決定するのに非常に役立つようです。

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
于 2016-09-09T07:53:20.677 に答える
80

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

クラスの舞台裏で行われていることが原因で、常にネイティブ C 実装よりも多少精度が低くなりますが、これは良い指標になるはずです。

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

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

于 2008-09-09T18:42:54.203 に答える
71

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

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

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

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

共通の参照を持つオブジェクトがある場合、正しい結果が得られ、シリアル化のサイズがメモリ内のサイズと常に一致するとは限らないことに注意してください。ただし、これは適切な概算値です。ByteArrayOutputStream のサイズを適切な値に初期化すると、コードが少し効率的になります。

于 2008-09-09T17:22:37.907 に答える
40

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のメモリを使用するまで、できるだけ多くの行を読み取る」を処理するロジックが必要だと述べたので、これは役立つかもしれないと思いました。

于 2008-09-09T17:19:28.130 に答える
22

他の回答の多くは、浅いサイズを提供します。たとえば、キーや値のない HashMap のサイズは、おそらくあなたが望むものではありません。

jamm プロジェクトは上記の java.lang.instrumentation パッケージを使用しますが、ツリーをたどるので、大量のメモリを使用できます。

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

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

于 2015-03-06T14:07:34.547 に答える
12

反射を使用してオブジェクトを歩かなければなりません。あなたがするように注意してください:

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

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

于 2008-09-09T17:19:48.177 に答える
12

ツールで測定するか、手で見積もる必要があり、使用している JVM によって異なります。

オブジェクトごとに一定のオーバーヘッドがあります。これは JVM 固有ですが、通常は 40 バイトと見積もっています。次に、クラスのメンバーを確認する必要があります。オブジェクト参照は、32 ビット (64 ビット) JVM では 4 (8) バイトです。プリミティブ タイプは次のとおりです。

  • ブール値とバイト: 1 バイト
  • char および short: 2 バイト
  • int と float: 4 バイト
  • long および double: 8 バイト

配列は同じ規則に従います。つまり、オブジェクト参照であるため、オブジェクトで 4 (または 8) バイトを使用し、その長さに要素のサイズを掛けます。

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

于 2008-09-09T17:20:40.187 に答える
6

この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() { }
}
于 2012-05-14T16:37:13.947 に答える
4

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

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 回の実行で必要なメモリ量を取得します。さらに、全体をより多く実行して平均を出すことができます。

于 2015-04-03T12:09:59.390 に答える
3

これは、圧縮された OOP で 32 ビット、64 ビット、および 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 );
    }
}
于 2014-06-26T05:36:47.960 に答える
2

それがあなたが求めているものであれば、メソッド呼び出しはありません。少し調べれば、自分で書くことができると思います。特定のインスタンスには、参照数とプリミティブ値に加えてインスタンス ブックキーピング データから派生した固定サイズがあります。オブジェクト グラフをたどるだけです。行の種類のバリエーションが少ないほど、簡単になります。

それが遅すぎるか、価値がないだけの場合は、古き良き行カウントの経験則が常にあります。

于 2008-09-09T17:15:40.120 に答える
2

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

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();
    }

}

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

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

アラインメントの動作とヒープ ブロック ヘッダーのオーバーヘッドの可能性を考えると、これは予想されることです。

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

于 2013-11-16T03:34:24.200 に答える
1

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

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

今、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 オブジェクト参照の場合がありますが、オブジェクトの作成によって必要なメモリは異なります。

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

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

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

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

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

于 2015-03-03T07:04:58.477 に答える
-6

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

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

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

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

別の戦略は、各オブジェクトを取得し、リフレクションによってそのメンバーを調べ、サイズ (boolean & byte = 1 バイト、short & char = 2 バイトなど) を合計して、メンバーシップ階層を下っていく方法です。しかし、それは面倒で費用がかかり、シリアライゼーション戦略と同じことをすることになります。

于 2008-09-09T17:11:35.763 に答える