例如,假设我有一个应用程序,可以读取包含大量数据行的 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 对象布局)是用于分析 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)。这只是该类的单个实例所消耗的空间;它不包括该类引用的任何其他对象。它 包括对象头、字段对齐和填充的虚拟机开销。为了 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)

“(其他)”条目 描述堆中不属于此对象图的其他对象.

最好的 jol 文档是 乔尔样品 在 jol 存储库中。这些示例演示了常见的 jol 操作,并展示了如何使用 jol 来分析 VM 和垃圾收集器内部结构。

几年前,Javaworld 曾 关于确定复合和潜在嵌套 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 大小初始化为合理的值,代码将会更高效。

我不小心找到了一个已经在JDK中的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 位)、填充、子类填充,可以在循环数据结构和数组上正确工作。你可以只编译这个 .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 或类似的东西 使用对象等于作为比较器 以消除无限循环。

@乔唐内尔:我喜欢您的解决方案的简单性,但许多对象不可序列化(因此这会引发异常),字段可以是瞬态的,并且对象可以覆盖标准方法。

你必须用工具来测量它,或者手动估计它,这取决于你使用的 JVM。

每个对象都有一些固定的开销。它是 JVM 特定的,但我通常估计 40 字节。然后你必须看看班级的成员。在 32 位(64 位)JVM 中,对象引用为 4 (8) 个字节。原始类型有:

  • 布尔值和字节:1字节
  • 字符和短:2字节
  • 整型和浮点型: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() { }
}

还有 内存测量器 工具(以前位于 谷歌代码, , 从今起 GitHub),这很简单并且在商业友好下发布 阿帕奇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,获得一次运行占用的内存量。更重要的是,您可以将整个过程运行更多次并得出平均值。

如果您需要的话,则没有方法调用。经过一些研究,我想你可以自己编写。特定实例具有从引用和原始值的数量加上实例簿记数据导出的固定大小。您只需遍历对象图即可。行类型的变化越少,就越容易。

如果这太慢或者只是比它的价值带来更多麻烦,那么总是有好的老式行计数经验法则。

我曾经写过一个快速测试来即时估计:

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)

我的答案是基于尼克提供的代码。该代码测量序列化对象占用的字节总数。所以这实际上测量了序列化内容+普通对象内存占用(例如,仅序列化 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;
    }    

}

我已经使用原始类型、字符串和一些简单的类测试了这个解决方案。也可能存在未涵盖的情况。


更新: 修改示例以支持数组对象的内存占用计算。

您可以生成堆转储(例如使用 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 位)操作系统

原语

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个字节)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() 运算符,即使有,它也只会计算对其他对象的引用的成本和基元的大小。

一种方法是将其序列化为文件并查看文件的大小,如下所示:

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

当然,这假设每个对象都是不同的,并且不包含对其他任何对象的非瞬态引用。

另一种策略是获取每个对象并通过反射检查其成员,并将大小相加(布尔值和字节 = 1 字节,短值和字符 = 2 字节等),沿着成员资格层次结构向下工作。但这既乏味又昂贵,并且最终会做与序列化策略相同的事情。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top