ChatGPT解决这个技术问题 Extra ChatGPT

在 Java 中,确定对象大小的最佳方法是什么?

我有一个应用程序可以读取包含大量数据行的 CSV 文件。我根据数据的类型给用户总结了行数,但是我想确保我不会读入太多的数据行而导致OutOfMemoryError。每行转换为一个对象。有没有一种简单的方法可以以编程方式找出该对象的大小?是否有定义 VM 的原始类型和对象引用有多大的参考?

现在,我的代码说读取多达 32,000 行,但我还希望代码说读取尽可能多的行,直到我使用 32MB 内存。也许这是一个不同的问题,但我仍然想知道。

我使用 mvn 配置添加了我的代理并在此处解释了如何:stackoverflow.com/a/36102269/711855

S
Sergey Vyacheslavovich Brunov

您可以使用 java.lang.instrument package

编译这个类并将其放入一个 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

@Stefan 不错的提示!您能告诉我,使用您描述的方法,byte[0]byte[1]byte[5]int[0]int[1]int[2] 的大小是多少?如果结果包括数组长度和内存对齐的开销,那就太好了。
我尝试了这个并得到了奇怪且无益的结果。字符串总是 32,不管大小。我认为这可能是指针大小,但对于我创建的另一个不可变类,我得到了 24。它适用于原语,但你并不需要一个程序来告诉你一个 char 有多大。
@Brel 此解决方案只是文档中指定的“指定对象消耗的存储量的近似值”。另外我想作者决定将 String 的大小设置为 32 字节(只有指针?),因为 Java 的 String 池,这使得很难说 String 实例是共享的(存储在池中)还是本地和唯一的类。
如果不导出 jar,我如何使用 ObjectSizeFetcher?我在eclipse中测试了java项目。
@brel 不管实际长度如何,字符串只有 32 个字节的原因是因为字符串的可变长度部分存储在 char[] 中,这是它自己的对象。要获得对象的真实大小,您需要将其自身的大小和它引用的每个对象的大小相加。
J
Jeffrey Bosboom

您应该使用 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-.]+$") 的结果(取自 this answer),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)

“(其他)”条目 describe other objects in the heap that are not part of this object graph

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


这个答案应该有更多的赞成票。绝对是一个很好的检查选择。编辑:检查这是在今年添加的,而在 08 年提出了问题。可能是目前执行 OP 要求的最佳和最简单的选择。
工具作者写了 a blog post about Jol
要确定对象“obj”的大小,请使用:org.openjdk.jol.info.GraphLayout.parseInstance(obj).totalSize();
请注意,vmDetails 现在是 VM.current().details()
查看 GraphLayout.parseInstance(instance).toFootprint() 我发现了解对象大小更有用
T
Tom

我无意中发现了一个 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

同样在这里,我正在尝试上面提出的其他解决方案并遇到了 ObjectSizeCalculator。我相信之前没有人提到过,因为它是最近在 JDK 8 上作为项目 Nashorn 的一部分引入的。但是我还没有在网上找到任何关于这个类的官方文档。
它似乎没有考虑字符串长度。它只是堆栈上的大小吗?
我有一个哈希图,其中 com.carrotsearch.RamUsageEstimator 返回大约一半的 ObjectSizeCalculator。哪一个是真的? - 哪个更可靠?
请注意,ObjectSizeCalculator 仅在 HotSpot VM 上受支持
JDK 11 上也不再存在 jdk.nashorn.internal.ir.debug.ObjectSizeCalculator
B
Boris Terzic

几年前 Javaworld 有 an article on determining the size of composite and potentially nested Java objects,他们基本上是在 Java 中创建 sizeof() 实现。该方法基本上建立在其他工作的基础上,人们通过实验确定基元和典型 Java 对象的大小,然后将这些知识应用于递归遍历对象图以计算总大小的方法。

仅仅因为类的幕后发生的事情,它总是比原生 C 实现更不准确,但它应该是一个很好的指标。

或者,一个名为 sizeof 的 SourceForge 项目提供了一个具有 sizeof() 实现的 Java5 库。

PS 不要使用序列化的方式,序列化对象的大小和它存活时消耗的内存量之间没有关联。


sizeof 实用程序可能是最快的方法。这基本上是 Stefan 所说的,但已经装在一个可以使用的罐子里了。
答案中的链接已损坏
u
u32i64

首先,“对象的大小”在 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 大小初始化为合理的值,则代码会更有效率。


我喜欢这种方法。就对象大小而言,您离我们有多远。
非常简单有效。其他方法太乱了(特别是在 Eclipse RCP 内部)。谢谢。
序列化不会跟踪瞬态变量,并且默认的序列化方法以 UTF-8 写入字符串,因此任何 ANSI 字符将只占用一个字节。如果您有很多字符串,那么您的大小将太远以至于毫无用处。
虽然这可能无法给出确切的大小,但出于我的需要,我只需要比较 2 个对象,并且 SizeOf 不会从 Web 应用程序初始化。谢谢!
YourKit的好推荐。其他选择是 VirtualVMjvmmonitor
Y
Yuval

如果您只想知道 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 内存之前读取尽可能多的行”的逻辑。


这不是一个好的解决方案,因为您永远不知道何时会发生垃圾收集,或者一次将多少额外内存分配给堆。
这是真的,我不打算用这个来解决这篇文章的主要问题,但它可能会帮助他以编程方式了解他何时接近达到最大堆大小。
此解决方案的其他问题是当您处于多线程环境中时(例如在 Web 服务器中)。其他线程可能正在执行并消耗内存。使用此近似值,您正在计算所有虚拟机中的已用内存。
另一个缺点是 freeMemory 返回一个近似值。尝试创建一个 javax.crypto.Cipher 对象。两次调用 freeMemory(估计密码的大小)之间的差异不是恒定的!
我相信你可以强制垃圾收集,所以你可以用这种方法做一些事情。
A
Attila Szegedi

当我在 Twitter 工作时,我编写了一个用于计算深度对象大小的实用程序。它考虑了不同的内存模型(32 位、压缩 oops、64 位)、填充、子类填充,可在循环数据结构和数组上正常工作。你可以只编译这个 .java 文件;它没有外部依赖:

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


齐亚!我也想大声喊出您的 presentation:幻灯片 15-20 非常适合帮助您直观地了解各种数据结构决策的成本。感谢您发布!
“它没有外部依赖” - 因为番石榴什么时候不是外部依赖?
Guave 是一个外部依赖项。
此解决方案在使用 OpenJDK 17 时不起作用
I
Ilya Serbis

许多其他答案提供了浅大小 - 例如没有任何键或值的 HashMap 的大小,这可能不是您想要的。

jamm 项目使用上面的 java.lang.instrumentation 包,但会遍历树,因此可以为您提供深度内存使用。

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

要使用 MemoryMeter,请使用“-javaagent:/jamm.jar”启动 JVM


s
simon04

使用 JetBrains IntelliJ 时,首先在文件 | 中启用“附加内存代理”。设置 |构建、执行、部署 |调试器。

https://i.stack.imgur.com/TvMel.png


我在 Intellij 中没有看到这一点 - 使用 2019.2。你用的是什么版本?
这个错误:已连接到目标 VM,地址:'127.0.0.1:49538',传输:'socket' JDWP 退出错误 AGENT_ERROR_OUT_OF_MEMORY(188): PushLocalFrame: Unable to push JNI frame [src/jdk.jdwp. agent/share/native/libjdwp/util.c:1560] 本地方法中的致命错误:JDWP PushLocalFrame:无法推送 JNI 帧,jvmtiError=AGENT_ERROR_OUT_OF_MEMORY(188) 与目标 VM 断开连接,地址:'127.0.0.1:49538' , transport: 'socket' 进程以退出代码 134 结束(被信号 6: SIGABRT 中断)
我正在使用 Android Studio Bumblebee (2021.1.1),但未显示 Calculate Retained Size。即使在选项中检查 Attach memory agent 之后。是否包含在 AS 中?我在 Intellij docs 中找到了它。
J
Jason Cohen

您必须使用反射来行走对象。这样做时要小心:

仅仅分配一个对象在 JVM 中有一些开销。数量因 JVM 而异,因此您可以将此值作为参数。至少让它成为一个常量(8 个字节?)并应用于任何分配的东西。

仅仅因为 byte 理论上是 1 字节并不意味着它只需要一个内存。

对象引用中会有循环,因此您需要保留一个 HashMap 或类似的对象,使用 object-equals 作为比较器来消除无限循环。

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


Java规范中没有定义各种原语的大小吗? (§2.4.1)
不是在“它占用多少内存”的意义上,这是一个问题。仅在它们如何运作的意义上。例如,bytes、chars 和 shorts 在 Java 堆栈上占据了整个单词,即使它们使用舍入等操作。
这听起来类似于测量尺寸,如 Heinz 在他的 Newsletter #78 中所示:javaspecialists.eu/archive/Issue078.html。我用过。他的方法奏效了。
e
erickson

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

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

布尔值和字节:1 个字节

char 和 short:2 个字节

整数和浮点数:4 个字节

long 和 double:8 个字节

数组遵循相同的规则;也就是说,它是一个对象引用,因此在您的对象中占用 4(或 8)个字节,然后将其长度乘以其元素的大小。

由于对垃圾收集器的异步调用等,尝试通过调用 Runtime.freeMemory() 以编程方式执行此操作不会给您带来太多的准确性。使用 -Xrunhprof 或其他工具分析堆将为您提供最准确的结果。


@erickson 我不确定 sizeof(boolean)==1 看这个线程(stackoverflow.com/questions/1907318/…)。你能对此发表评论吗?
@dma_k,Java 实际上没有真正的布尔值。布尔值的大小是数组外 4 字节和 boolean[] 内 1 字节。实际上所有非双/长类型的原语都是 4 个字节。后者是 8(答案也错误地将它们设为 4)
@bestsss:更准确地说,最小的内存分配取决于 JVM 的平台和实现。堆上的对象也是对齐的,所以在总结所有大小之后,需要四舍五入。
C
Community

还有 Memory Measurer 工具(以前在 Google Code,现在在 GitHub),它很简单,在商业友好的 Apache 2.0 许可 下发布,如similar question 中讨论过。

如果您想测量内存字节消耗,它也需要 java 解释器的命令行参数,但其他方面似乎工作得很好,至少在我使用它的场景中。


M
Miguel Gamboa

java.lang.instrument.Instrumentation 类提供了一种获取 Java 对象大小的好方法,但它要求您定义 premain 并使用 Java 代理运行您的程序。当您不需要任何代理时,这非常无聊,然后您必须为您的应用程序提供一个虚拟 Jar 代理。

因此,我使用 sun.misc 中的 Unsafe 类获得了替代解决方案。因此,根据处理器架构考虑对象堆对齐并计算最大字段偏移量,您可以衡量 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() { }
}

有趣的方法,但这不是假设对象及其字段存储不是碎片化的吗?
是的,我不知道有任何 JVM 实现会产生这种碎片。
我不明白。碎片不是一个选项:) 让我们以对象 C 为例,它存储为对象 A 和 B 的字段。它不会在 A 或 B 中转移整个事物吗?
对不起,但我不理解你的观点。根据我的解释,Java 中的对象不能存储在其他对象中,就像 C 结构或 .Net 中的值类型一样。因此,当您说:“对象 C 存储为对象 A 和 B 的字段”时,这意味着对象 A 和 B 具有存储对对象 C 的引用(指针)的字段。那么 A 和 B 的大小等于该字段的偏移量加上指向对象 C 的引用(指针)的大小。引用的大小是一个单词的大小。
哦,好的,我们正在谈论浅尺寸。我的错。
r
reallynice

无需弄乱仪器等,如果您不需要知道对象的字节精确大小,您可以使用以下方法:

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

do your job here

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

通过这种方式,您可以在之前和之后读取已用内存,并在获取已用内存之前调用 GC,您可以将“噪音”几乎降低到 0。

为了获得更可靠的结果,您可以运行您的作业 n 次,然后将使用的内存除以 n,获得一次运行占用的内存量。更重要的是,您可以将整个过程运行更多次并取平均值。


System.gc() 不只是通知您要 GC 吗?不能保证完全调用 GC。
@非常好。这是不安全的,因为你可能永远不会 GC 做什么或影响你的行之间的内存。因此,“在”两个 freeMemory 方法之间,GC 可以释放更多您不考虑的空间,因此您的对象看起来更小
@MertSerimer“不安全”对我来说是完全不同的水平:正如我也说过的,最多这不是那么准确。此外,您不能驱动 GC(如 Raildex 所述),但对于这种情况,我也建议在循环中插入它。如前所述,这只是一个快速、肮脏和近似的系统,如果结果不需要非常可靠,它就可以工作。
这有很多问题,但它确实给你一个很好的赃物。
D
David Ryan

我正在寻找满足以下要求的对象大小的运行时计算:

在运行时可用,无需包含检测。

与 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;
}

}


G
Gwenc37

这是我使用一些链接示例制作的实用程序,用于处理带有压缩 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 );
    }
}

你用值测试过这个类吗?我试过了,但对我来说,值不正确!!!。
它给我的一个简单对象的值大约是正确的,但对于包含 1mio 对象的列表,它的值相差了 10 倍。不过,非常好的工作!
有趣的。我已经使用 JDK7u67 在 Windows 7 x64 和 Linux 2.6.16/x86_64 上使用 32bit/64bit/oop 地址模式中的每一种对其进行了测试。我将它与 Eclipse Memory Analyzer 1.3.x 中分析的内存转储进行了比较。你用的是什么设置?你有我可以尝试的具体例子吗?
我能做的最好的选择。我不能使用 Instrumentation 因为我没有启动 tomcat,ObjectSizeCalculator 因为不确定 VM 类型(HotSpot)和 JOL bacouse spring beans。我使用它并添加第二个参数来忽略单例即 AbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex() 并重构 internalSizeOf 代码以忽略 Class 和 Enum
为了比较结果,请使用 ObjectSizeCalculator(计算整个服务器 1GB 到 10 秒)。 JOL 导致 MemError(6GB 不够),我没有得到相同的结果,可能是因为枚举。
s
sblundy

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

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


J
Jason C

我写了一个快速测试来即时估计:

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

考虑到对齐行为和可能的堆块头开销,这是我们所期望的。

此处接受的答案中详述的检测方法最准确。我描述的方法是准确的,但仅在没有其他线程创建/丢弃对象的受控条件下。


A
ACV

只需使用 java 可视 VM。

它拥有分析和调试内存问题所需的一切。

它还有一个 OQL(对象查询语言)控制台,可让您做许多有用的事情,其中之一是 sizeof(o)


g
granadaCoder

一个可能的 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)

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

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

size 为您提供由于创建对象而增加的 jvm 内存使用量,这通常是对象的大小。


// 如果 GC 在对象构造代码期间运行在中间怎么办?现在可能一直产生正确的结果。
A
Agnius Vasiliauskas

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

}

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

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


K
Kanagavelu Sugumar

这个答案与对象大小无关,而是当您使用数组来容纳对象时;它将为对象分配多少内存大小。

因此,所有这些集合的数组、列表或映射都不会真正存储对象(仅在基元时,需要真正的对象内存大小),它只会存储这些对象的引用。

现在 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 字节)


一个 4 字节的整数和一个未知大小的对象引用如何放入 4 个字节?
@EJP 我的意思是说所有对象 REFERENCE 只需要 4 个字节的内存。它可能是字符串引用或双对象引用,但取决于对象的创建,所需的内存会有所不同。
J
JZeeb

您可以生成堆转储(例如使用 jmap),然后分析输出以查找对象大小。这是一个离线解决方案,但您可以检查浅尺寸和深尺寸等。


A
Ali Dehghani

假设我声明了一个名为 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

M
Maurice

如果您的应用程序将 Apache commons lang library 作为依赖项或正在使用 Spring framework,那么您还可以使用 SerializationUtils 类快速找出任何给定对象的近似字节大小。

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

序列化会增加开销吗?似乎序列化的字节大小取决于变量名的长度
S
Sai Kiran

对于 JSONObject,以下代码可以帮助您。

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

返回大小(以字节为单位)

我通过将 JSONArray 对象写入文件来检查它。它给出了对象的大小。


这仅适用于主要是字符串的对象。
j
jodonnell

我怀疑您是否想以编程方式执行此操作,除非您只想执行一次并将其存储以备将来使用。这是一件代价高昂的事情。 Java 中没有 sizeof() 运算符,即使有,它也只会计算对其他对象的引用的成本和原语的大小。

您可以这样做的一种方法是将事物序列化为文件并查看文件的大小,如下所示:

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

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

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


我会使用 ByteArrayOutputStream 将其序列化为 byte[]。这比将其写入文件要快得多。
@KorayTugay 确定对象的字节大小已经是一项昂贵的操作。将每个对象写入磁盘以确定大小,只是让它爬行......
序列化的对象格式与堆内存中对象的格式完全不同。最值得注意的是,对象类(及其所有可序列化的超类)的描述符被写入流中。因此,编写一个简单的 java.lang.Integer 实例会产生大约 80 个字节,其中堆表示通常是 32(与对象流表示不同,堆表示取决于指针大小和对象对齐)。相反,序列化的 null 引用需要一个字节,而不是堆内存中的四个或八个字节。