首页 文章

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

提问于
浏览
553

例如,让's say I have an application that can read in a CSV file with piles of data rows. I give the user a summary of the number of rows based on types of data, but I want to make sure that I don' t读入太多数据行并导致 OutOfMemoryError s . 每行都转换为一个对象 . 有没有一种简单的方法可以通过编程方式找出该对象的大小?是否有一个引用定义了 VM 的原始类型和对象引用的大小?

现在,我的代码说读到了 32,000 rows ,但我'd also like to have code that says read as many rows as possible until I'已经使用了 32MB 的内存 . 也许这是一个不同的问题,但我仍然想知道 .

24 回答

  • 8

    您应该使用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)
    

    "(something else)"条目describe other objects in the heap that are not part of this object graph .

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

  • 6

    我不小心发现了一个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
    
  • 7

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

  • 6

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

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

    new MemoryMeter().measureDeep(myHashMap);
    

    https://github.com/jbellis/jamm

  • 18

    我写了一个快速测试,以便即时估算:

    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() ,其中 requests GC runs and waits for the reported free heap size to stabilize . 以上的输出是:

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

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

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

  • 3

    您必须使用工具进行测量,或者手动估算它,这取决于您使用的JVM .

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

    • 布尔和字节:1个字节

    • char和short:2个字节

    • int和float:4个字节

    • 长和双:8个字节

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

    尝试通过调用 Runtime.freeMemory() 以编程方式执行此操作只是因为对垃圾收集器的异步调用等而无法提供更多准确性 . 使用-Xrunhprof或其他工具对堆进行分析将为您提供最准确的结果 .

  • 0

    你可以使用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
    
  • 49

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

    所以唯一正确的方法是向JVM询问一个好的分析器(我使用YourKit),这可能不是你想要的 .

    但是,从上面的描述中可以看出每行都是自包含的,并且没有大的依赖树,因此序列化方法可能是大多数JVM的良好近似 . 最简单的方法如下:

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

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

  • 13

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

  • 71

    只需使用java visual VM .

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

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

  • 35

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

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

    或者是SourceForge适当调用sizeof的项目,它提供了一个带有sizeof()实现的Java5库 .

    附:不要使用序列化方法,序列化对象的大小与它在活动时消耗的内存量之间没有关联 .

  • 3

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

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

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

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

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

  • 74

    我的回答是基于尼克提供的代码 . 该代码测量序列化对象占用的总字节数 . 所以这实际上测量了序列化的东西普通对象内存占用(只是序列化例如 int ,你会看到序列化字节的总量不是 4 ) . 因此,如果您希望获得完全用于对象的原始字节数 - 您需要稍微修改该代码 . 像这样:

    import java.io.ByteArrayOutputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    public class ObjectSizeCalculator {
        private Object getFirstObjectReference(Object o) {
            String objectType = o.getClass().getTypeName();
    
            if (objectType.substring(objectType.length()-2).equals("[]")) {
                try {
                    if (objectType.equals("java.lang.Object[]"))
                        return ((Object[])o)[0];
                    else if (objectType.equals("int[]"))
                        return ((int[])o)[0];
                    else
                        throw new RuntimeException("Not Implemented !");
                } catch (IndexOutOfBoundsException e) {
                    return null;
                }
            }
    
            return o;
        } 
    
        public int getObjectSizeInBytes(Object o) {
            final String STRING_JAVA_TYPE_NAME = "java.lang.String";
    
            if (o == null)
                return 0;
    
            String objectType = o.getClass().getTypeName();
            boolean isArray = objectType.substring(objectType.length()-2).equals("[]");
    
            Object objRef = getFirstObjectReference(o);
            if (objRef != null && !(objRef instanceof Serializable))
                throw new RuntimeException("Object must be serializable for measuring it's memory footprint using this method !");
    
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(o);
                oos.close();
                byte[] bytes = baos.toByteArray();
    
                for (int i = bytes.length - 1, j = 0; i != 0; i--, j++) {
                    if (objectType != STRING_JAVA_TYPE_NAME) {
                        if (bytes[i] == 112)
                            if (isArray)
                                return j - 4;
                            else
                                return j;
                    } else {
                        if (bytes[i] == 0)
                            return j - 1;
                    }
                }
            } catch (Exception e) {
                return -1;
            }
    
            return -1;
        }    
    
    }
    

    我用原始类型String测试了这个解决方案,并在一些普通的类上测试过 . 可能还没有涵盖的案例 .

    UPDATE: 已修改示例以支持数组对象的内存占用计算 .

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

    size会因为对象创建而增加jvm的内存使用量,通常是对象的大小 .

  • -6

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

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

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

    • (4/8字节)取决于(32/64位)OS

    PRIMITIVES

    int   [] intArray    = new int   [1]; will require 4 bytes.
    long  [] longArray   = new long  [1]; will require 8 bytes.
    

    OBJECTS

    Object[] objectArray = new Object[1]; will require 4 bytes. The object can be any user defined Object.
    Long  [] longArray   = new Long  [1]; will require 4 bytes.
    

    我的意思是说所有对象REFERENCE只需要4个字节的内存 . 它可能是String引用或Double对象引用,但依赖于对象创建所需的内存会有所不同 .

    例如)如果我为下面的类 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字节)

  • 2

    还有 Memory Measurer 工具(以前在Google Code,现在在GitHub上),这很简单并且在商业友好型 Apache 2.0 license 下发布,如similar question中所述 .

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

  • 10

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

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

    这样你就可以在之前和之后读取使用过的内存,并在获取已用内存之前调用GC,将“噪音”降低到几乎为0 .

    为了获得更可靠的结果,您可以运行您的作业n次,然后将使用的内存除以n,从而获得一次运行所需的内存量 . 更重要的是,你可以更多次地运行整个事情并取得平均值 .

  • 424

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

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

    以字节为单位返回大小

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

  • 2

    你必须使用反射来对象 . 你要小心:

    • 只是分配一个对象在JVM中有一些开销 . 数量因JVM而异,因此您可以将此值设为参数 . 至少使它成为常量(8个字节?)并应用于任何已分配的内容 .

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

    • 对象引用中会有循环,所以你需要保持一个 HashMap 或某些使用object-equals作为比较器以消除无限循环 .

    @jodonnell:我喜欢你的解决方案的简单性,但许多对象不是Serializable(所以这会抛出异常),字段可以是瞬态的,对象可以覆盖标准方法 .

  • 58

    我推荐 carrotsearch 的java-sizeof库 . 这很简单 .

    你可以在maven中得到它:

    <dependency>
        <groupId>com.carrotsearch</groupId>
        <artifactId>java-sizeof</artifactId>
        <version>0.0.3</version>
    </dependency>
    

    只返回一个对象字节的代码行:

    RamUsageEstimator.sizeOf(new Object());
    

    你可以在Github上看到源代码

    here是图书馆作者的演示文稿 .

  • -4

    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() { }
    }
    
  • 0

    没有方法调用,如果这是你要求的 . 通过一些研究,我想你可以写自己的 . 特定实例具有固定大小,其源自引用和原始值的数量加上实例簿记数据 . 您只需走对象图 . 行类型越少,越容易 .

    如果这太慢或者只是比它的 Value 更麻烦,那么总是有很好的老式行计数规则 .

  • 0

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

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

  • 2

    这是我使用一些链接的示例来处理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 );
        }
    }
    

相关问题