簡體   English   中英

在 Java 中,確定 object 大小的最佳方法是什么?

[英]In Java, what is the best way to determine the size of an object?

我有一個應用程序可以讀取包含大量數據行的 CSV 文件。 我根據數據類型向用戶提供了行數的摘要,但我想確保我不會讀入過多的數據行並導致OutOfMemoryError s。 每行轉換為 object。 有沒有一種簡單的方法可以以編程方式找出 object 的大小? 是否有參考定義了VM的原始類型和 object 參考有多大?

現在,我的代碼說 read up to 32,000 rows ,但我也希望代碼說 read as many rows as possible 直到我使用了32MB的 memory。 也許這是一個不同的問題,但我仍然想知道。

您可以使用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

您應該使用jol ,這是作為 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 )獲取對象實例的淺層大小。 這只是該類的單個實例消耗的空間; 它不包括該類引用的任何其他對象。 確實包括對象標頭、字段對齊和填充的 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()GraphLayout.parseInstance(obj).toPrintable()會告訴您每個引用對象的字段取消引用的地址、大小、類型、值和路徑,盡管這通常是太多細節GraphLayout.parseInstance(obj).toPrintable()用。 對於正在進行的模式示例,您可能會得到以下信息。 (地址可能會在兩次運行之間發生變化。)

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 和垃圾收集器內部結構。

無意中發現了一個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

幾年前 Javaworld 有 一篇關於確定復合和潛在嵌套 Java 對象的大小的文章,他們基本上介紹了在 Java 中創建 sizeof() 實現的過程。 該方法基本上建立在其他工作之上,人們通過實驗確定基元和典型 Java 對象的大小,然后將這些知識應用於遞歸遍歷對象圖以計算總大小的方法。

它總是會比本地 C 實現稍微不准確,因為在類的幕后發生的事情,但它應該是一個很好的指標。

或者,一個名為sizeof的 SourceForge 項目提供了一個帶有 sizeof() 實現的 Java5 庫。

PS 不要使用序列化的方法,序列化對象的大小與其存活時消耗的內存量之間沒有相關性。

首先,“對象的大小”在 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 大小初始化為一個合理的值,代碼會更有效率。

如果您只想知道您的 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 位)、填充、子類填充,在循環數據結構和數組上正常工作。 你可以編譯這個 .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 個字節並不意味着它在內存中只需要一個字節。
  • 對象引用中會有循環,因此您需要保留一個HashMap或類似的對象,使用 object-equals 作為比較器來消除無限循環。

@jodonnell:我喜歡您的解決方案的簡單性,但許多對象不是可序列化的(因此這會引發異常),字段可以是瞬態的,並且對象可以覆蓋標准方法。

你必須用工具測量它,或者手工估計它,這取決於你使用的JVM。

每個對象有一些固定的開銷。 它是特定於 JVM 的,但我通常估計 40 個字節。 然后你必須看看班級的成員。 在 32 位(64 位)JVM 中,對象引用是 4(8)個字節。 原始類型是:

  • 布爾值和字節:1 字節
  • char 和 short:2 個字節
  • int 和 float:4 字節
  • long 和 double:8 字節

數組遵循相同的規則; 也就是說,它是一個對象引用,因此它在您的對象中占用 4(或 8)個字節,然后它的長度乘以其元素的大小。

嘗試通過調用Runtime.freeMemory()以編程方式執行此操作並不會為您提供太多准確性,因為對垃圾收集器的異步調用等。使用 -Xrunhprof 或其他工具分析堆將為您提供最准確的結果。

使用 JetBrains IntelliJ 時,首先在 File | 中啟用“Attach memory agent” 設置 | 構建、執行、部署 | 調試器。

調試時,右鍵單擊感興趣的變量並選擇“計算保留大小”: 計算保留大小

還有Memory Measurer工具(以前在Google Code ,現在在GitHub 上),它很簡單,並在商業友好的Apache 2.0 許可下發布,如在類似問題中所討論的。

如果您想測量內存字節消耗,它也需要 java 解釋器的命令行參數,但其他方面似乎工作得很好,至少在我使用它的場景中。

java.lang.instrument.Instrumentation類提供了一種獲取 Java 對象大小的好方法,但它需要您定義一個premain並使用 Java 代理運行您的程序。 當您不需要任何代理,然后您必須為您的應用程序提供一個虛擬 Jar 代理時,這是非常無聊的。

所以我使用sun.miscUnsafe類得到了一個替代解決方案。 因此,根據處理器架構考慮對象堆對齊並計算最大字段偏移量,您可以衡量一個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() { }
}

不必搞亂儀器等,如果您不需要知道對象的字節精確大小,您可以采用以下方法:

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

do your job here

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

通過這種方式,您可以在前后讀取已用內存,並在獲得已用內存之前調用 GC,您將“噪音”幾乎降低到 0。

為了獲得更可靠的結果,您可以將作業運行 n 次,然后將使用的內存除以 n,從而獲得一次運行所需的內存。 更重要的是,您可以將整個過程運行更多次並取平均值。

我正在尋找滿足以下要求的對象大小的運行時計算:

  • 在運行時可用,無需包含檢測。
  • 適用於 Java 9+,無需訪問 Unsafe。
  • 僅基於類。 不是考慮字符串長度、數組長度等的深度 sizeOf。

以下內容基於原始 Java 專家文章 ( https://www.javaspecialists.eu/archive/Issue078.html ) 的核心代碼以及此問題的另一個答案中 Unsafe 版本的一些內容。

我希望有人覺得它有用。

public class JavaSize {

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 HEADER_SIZE = 8;

public static int sizeOf(Class<?> clazz) {
    int result = 0;

    while (clazz != null) {
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            if (!Modifier.isStatic(fields[i].getModifiers())) {
                if (fields[i].getType().isPrimitive()) {
                    Class<?> primitiveClass = fields[i].getType();
                    if (primitiveClass == boolean.class || primitiveClass == byte.class) {
                        result += 1;
                    } else if (primitiveClass == short.class) {
                        result += 2;
                    } else if (primitiveClass == int.class || primitiveClass == float.class) {
                        result += 4;
                    } else if (primitiveClass == double.class || primitiveClass == long.class) {
                        result += 8;
                    }

                } else {
                    // assume compressed references.
                    result += 4;
                }
            }
        }

        clazz = clazz.getSuperclass();

        // round up to the nearest WORD length.
        if ((result % WORD) != 0) {
            result += WORD - (result % WORD);
        }
    }

    result += HEADER_SIZE;

    return result;
}

}

這是我使用一些鏈接示例制作的實用程序,用於處理帶有壓縮 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 );
    }
}

我曾經寫了一個快速測試來即時估計:

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)

如果這就是您所要求的,則沒有方法調用。 通過一些研究,我想你可以自己寫。 特定實例具有從引用和原始值的數量以及實例簿記數據導出的固定大小。 您只需遍歷對象圖即可。 行類型的變化越少,越容易。

如果這太慢或只是比它的價值更麻煩,那么總是有很好的老式行計數經驗法則。

一個可能的 2022 年答案。

https://github.com/ehcache/sizeof

https://mvnrepository.com/artifact/org.ehcache/sizeof

https://mvnrepository.com/artifact/org.ehcache/sizeof/0.4.0

0.4.0 版只有一個(編譯)依賴於

https://mvnrepository.com/artifact/org.slf4j/slf4j-api

這是一件好事。

示例代碼:

//import org.ehcache.sizeof.SizeOf;

SizeOf sizeOf = SizeOf.newInstance(); // (1)
long shallowSize = sizeOf.sizeOf(someObject); // (2)
long deepSize = sizeOf.deepSizeOf(someObject); // (3)
long heapSizeBefore = Runtime.getRuntime().totalMemory();

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

size 使您由於創建對象而增加了 jvm 的內存使用量,這通常是對象的大小。

我的回答是基於 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 和一些簡單的類測試了這個解決方案。 也可能有不承保的情況。


更新:修改示例以支持數組對象的內存占用計算。

這個答案與對象大小無關,但是當您使用數組來容納對象時; 它將為對象分配多少內存大小。

因此,數組、列表或映射所有這些集合不會真正存儲對象(僅在原始對象時,才需要真正的對象內存大小),它只會存儲這些對象的引用。

現在Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8 字節)取決於(32/64 位)操作系統

原語

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 個字節的內存。 它可能是字符串引用或雙對象引用,但取決於對象的創建,所需的內存會有所不同。

例如)如果我為下面的類ReferenceMemoryTest創建對象,那么將創建 4 + 4 + 4 = 12 字節的內存。 當您嘗試初始化引用時,內存可能會有所不同。

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

因此,在創建對象/引用數組時,其所有內容都將被 NULL 引用占用。 我們知道每個引用需要 4 個字節。

最后,以下代碼的內存分配為 20 字節。

ReferenceMemoryTest ref1 = new ReferenceMemoryTest(); ( 4(ref1) + 12 = 16 字節) ReferenceMemoryTest ref2 = ref1; ( 4(ref2) + 16 = 20 字節)

您可以生成堆轉儲(例如使用 jmap),然后分析輸出以查找對象大小。 這是一個離線解決方案,但您可以檢查淺層和深層尺寸等。

假設我聲明了一個名為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

If your application has the Apache commons lang library as a dependency or is using the Spring framework then you can also use the SerializationUtils class to quickly find out the approximate byte size of any given object.

byte[] data = SerializationUtils.serialize(user);
System.out.println("Approximate object size in bytes " + data.length);

對於 JSONObject,以下代碼可以幫助您。

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

以字節為單位返回大小

我通過將它寫入文件來使用我的 JSONArray 對象檢查它。 它給出了對象大小。

我懷疑您是否想以編程方式執行此操作,除非您只想執行一次並將其存儲以備將來使用。 這是一件代價高昂的事情。 Java 中沒有 sizeof() 運算符,即使有,它也只會計算對其他對象的引用成本和原語的大小。

您可以這樣做的一種方法是將事物序列化為文件並查看文件的大小,如下所示:

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

當然,這假設每個對象都是不同的,並且不包含對其他任何對象的非瞬態引用。

另一種策略是獲取每個對象並通過反射檢查其成員並將大小相加(boolean & byte = 1 byte,short & char = 2 bytes 等),按照成員層次結構向下工作。 但這既乏味又昂貴,並且最終會做與序列化策略相同的事情。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM