代码之家  ›  专栏  ›  技术社区  ›  Jay R.

在爪哇,确定物体大小的最佳方法是什么?

  •  558
  • Jay R.  · 技术社区  · 16 年前

    例如,假设我有一个应用程序可以用成堆的数据行在csv文件中读取。我会根据数据类型向用户提供行数的摘要,但我要确保不会读取太多的数据行并导致 OutOfMemoryError 每行翻译成一个对象。有没有一种简单的方法可以通过编程的方式找出那个对象的大小?是否有一个引用定义基元类型和对象引用对于 VM ?

    现在,我有一个代码说读到 32000行 ,但我也希望让代码尽可能多地读取行,直到使用 32兆字节 记忆。也许这是一个不同的问题,但我还是想知道。

    23 回复  |  直到 6 年前
        1
  •  429
  •   Ravindra babu    8 年前

    你可以使用 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
    
        2
  •  81
  •   Jeffrey Bosboom    9 年前

    你应该使用 jol 作为OpenJDK项目的一部分开发的工具。

    JOL(Java对象布局)是JVMs中分析对象布局方案的一个小工具箱。这些工具大量使用不安全、JVMTI和可服务性代理(SA)来解码实际的对象布局、示意图和引用。这使得jol比其他依赖堆转储、规范假设等的工具更精确。

    要获取基本体、引用和数组元素的大小,请使用 VMSupport.vmDetails() . 在64位Windows上运行的Oracle JDK 1.8.0_上(用于以下所有示例),此方法返回

    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个是模式实例本身。

    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 samples 在Jol存储库中。这些示例演示了常见的jol操作,并展示了如何使用jol分析VM和垃圾收集器内部。

        3
  •  71
  •   Boris Terzic    16 年前

    几年前Javaworld an article on determining the size of composite and potentially nested Java objects 它们基本上是通过在爪哇中创建一个siZeFor()实现的。该方法基本上建立在其他实验中,人们在实验中识别原语和典型Java对象的大小,然后将该知识应用于递归地遍历对象图以统计总大小的方法。

    它总是比本机C实现更不准确,这仅仅是因为类背后发生了一些事情,但是它应该是一个很好的指示器。

    或者,可以适当地调用sourceforge项目 sizeof 它提供了一个带有sizeof()实现的Java5库。

    P.S.不要使用序列化方法,序列化对象的大小和它在活动时消耗的内存量之间没有相关性。

        4
  •  58
  •   u32i64 Lennin    6 年前

    首先,“对象的大小”在Java中不是一个定义良好的概念。你可以指的是对象本身,只有它的成员,对象和它所指的所有对象(参考图)。您可以是指内存大小或磁盘大小。并且允许JVM优化字符串之类的东西。

    所以唯一正确的方法是使用一个好的探查器(我使用 YourKit ,这可能不是你想要的。

    但是,从上面的描述来看,似乎每一行都是独立的,并且没有大的依赖树,因此序列化方法可能是大多数JVM的一个很好的近似方法。最简单的方法如下:

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

    记住,如果有具有公共引用的对象,则 不会 给出正确的结果,序列化的大小并不总是与内存中的大小匹配,但这是一个很好的近似值。如果您将bytearrayOutputstream大小初始化为一个合理的值,那么代码将更加有效。

        5
  •  52
  •   Tom    8 年前

    我偶然发现了一个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
    
        6
  •  35
  •   Yuval    10 年前

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

        7
  •  18
  •   Attila Szegedi    9 年前

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

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

        8
  •  13
  •   rich    9 年前

    许多其他答案都提供了较浅的大小——例如,没有任何键或值的哈希图的大小,这不太可能是您想要的。

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

    new MemoryMeter().measureDeep(myHashMap);
    

    https://github.com/jbellis/jamm

        9
  •  10
  •   Jason Cohen    16 年前

    你必须使用反射来移动物体。像你这样小心:

    • 仅仅分配一个对象在JVM中就有一些开销。数量因JVM而异,因此您可以将该值作为参数。至少使它成为一个常量(8字节?)并适用于任何分配。
    • 只是因为 byte 理论上1字节并不意味着它只需要一个内存。
    • 对象引用中会有循环,因此需要 HashMap 或一些 使用object equals作为比较器 消除无限循环。

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

        10
  •  8
  •   erickson    12 年前

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

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

    • 布尔值和字节:1字节
    • 字符和短:2字节
    • int和float:4字节
    • 长和双字节:8字节

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

    尝试通过调用 Runtime.freeMemory() 因为对垃圾收集器的异步调用等原因,所以不会给您提供太多的准确性。使用-xrunhprof或其他工具分析堆将给您最准确的结果。

        11
  •  6
  •   Miguel Gamboa    12 年前

    这个 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() { }
    }
    
        12
  •  6
  •   Community Reversed Engineer    7 年前

    还有 内存测量器 工具(以前在 Google Code 现在 GitHub ,简单易懂,以商业友好的方式出版。 Apache 2.0许可证 ,如a中所述 similar question .

    如果你想测量内存字节消耗,它也需要命令行参数到Java解释器,但是在我使用它的场景中,它看起来很好。

        13
  •  3
  •   Gwenc37 Anand Shukla    10 年前

    这里有一个实用程序,我使用一些链接的例子来处理32位、64位和64位的压缩OOP。它使用 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 );
        }
    }
    
        14
  •  3
  •   reallynice Stefan Gehrig    9 年前

    不必处理插入等问题,如果不需要知道对象的字节确切大小,可以使用以下方法:

    System.gc();
    Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
    
    do your job here
    
    System.gc();
    Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
    

    这样,您可以在读取之前和之后读取已用内存,并在获取已用内存之前调用GC,从而将“噪声”几乎降低到0。

    为了获得更可靠的结果,您可以运行作业n次,然后将使用的内存除以n,得到一次运行需要多少内存。更重要的是,你可以把整件事情做更多次,并得出平均值。

        15
  •  2
  •   sblundy    16 年前

    如果你要求的是方法调用,那么就没有方法调用。通过一点研究,我想你可以自己写。一个特定的实例有一个固定的大小,这个大小是由引用的数量和原语值加上实例簿记数据得出的。您只需浏览对象图。行类型的变化越小,就越容易。

    如果这太慢或只是比它的价值更多的麻烦,总是有很好的老式的排数规则的拇指。

        16
  •  2
  •   Jason C    11 年前

    我曾经写过一个快速测试来评估:

    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
    

    这就是我们所期望的,给定对齐行为和可能的堆块头开销。

    这里的仪器方法在公认的答案中是最准确的。我描述的方法是准确的,但仅在没有其他线程创建/丢弃对象的受控条件下。

        17
  •  2
  •   ACV    6 年前

    只需使用Java Visual VM即可。

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

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

        18
  •  1
  •   Agnius Vasiliauskas    10 年前

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

    我已经用基本类型、字符串和一些普通类测试了这个解决方案。也可能没有涵盖的案例。


    更新: 为支持数组对象的内存足迹计算而修改的示例。

        19
  •  0
  •   JZeeb    11 年前

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

        20
  •  0
  •   user835199    10 年前
    long heapSizeBefore = Runtime.getRuntime().totalMemory();
    
    // Code for object construction
    ...
    long heapSizeAfter = Runtime.getRuntime().totalMemory();
    long size = heapSizeAfter - heapSizeBefore;
    

    由于对象的创建,大小会增加JVM的内存使用量,这通常是对象的大小。

        21
  •  0
  •   Kanagavelu Sugumar    9 年前

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

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

    现在 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.
    

    我的意思是说所有的对象引用只需要4个字节的内存。它可以是字符串引用或双对象引用,但取决于对象创建,所需的内存将有所不同。

    例如,如果我为下面的类创建对象 ReferenceMemoryTest 然后将创建4+4+4=12字节的内存。当您试图初始化引用时,内存可能会有所不同。

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

    因此,在创建对象/引用数组时,它的所有内容都将被空引用占据。我们知道每个引用需要4个字节。

    最后,下面代码的内存分配是20字节。

    referenceMemoryTest ref1=new referenceMemoryTest();(4(ref1)+12=16字节) referenceMemoryTest ref2=ref1;(4(ref2)+16=20字节)

        22
  •  -4
  •   solokiran    6 年前

    对于jsonObject,下面的代码可以帮助您。

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

    返回字节大小

    我用JSONarray对象检查了它,并将其写入一个文件。它给出了物体的大小。

        23
  •  -5
  •   jodonnell    16 年前

    我怀疑你是否想以编程的方式来做,除非你只想做一次,然后把它存储起来以备将来使用。这是一件昂贵的事情。在Java中没有siZOFF()运算符,即使存在,也只计算引用到其他对象的成本和基元的大小。

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

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

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

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