以编程方式计算Java对象占用的内存,包括它引用的对象

时间:2009-04-16 17:54:48

标签: java memory-management

我需要以编程方式确切地找出给定Java对象占用的内存量,包括它引用的对象所占用的内存。

我可以使用工具生成内存堆转储并分析结果。但是,生成堆转储需要花费大量时间,并且需要这样的工具来读取转储以生成报告。鉴于我可能需要多次执行此操作,如果我可以在项目中输入一些代码“运行时”,那么我的工作会更灵活。

我怎样才能做到最好?

ps:具体来说,我有一个javax.xml.transform.Templates类型的对象集合

3 个答案:

答案 0 :(得分:11)

您需要使用反射。由此产生的代码对于我来说太复杂了(尽管它很快将作为我正在构建的GPL工具包的一部分提供),但主要的想法是:

  • 对象标头使用8个字节(用于类指针和引用计数)
  • 每个原始字段使用1,2,4或8个字节,具体取决于实际类型
  • 每个对象引用(即非原始)字段使用4个字节(引用,加上引用的对象使用的任何内容)

您需要单独处理数组(8个字节的标头,4个字节的长度字段,4个*长度的字节表,以及内部使用的任何对象)。您需要使用反射处理迭代字段(以及它的父字段)的其他类型的对象。

您还需要在递归过程中保留一组“看到”的对象,以免多次在多个位置引用对象。

答案 1 :(得分:6)

看起来已经存在一个名为Classmexer的实用程序。

我自己没有尝试过,但在推出自己的路线之前,我会走那条路。

答案 2 :(得分:3)

一个好的通用解决方案是使用堆大小增量。这涉及最小的努力,并且可在任何类型的对象/对象图之间重复使用。通过多次实例化和销毁对象并在其间收集垃圾,然后取平均值,可以避免编译器和JVM优化,从而改变结果并获得相当准确的结果。如果您需要一个完整的答案,那么这可能不是您的解决方案,但对于我所知道的所有实际应用程序(分析,内存需求计算),它的工作非常好。下面的代码就是这样做的。

    public class Sizeof {
      public static void main(String[] args)
          throws Exception {
        // "warm up" all classes/methods that we are going to use:
        runGC();
        usedMemory();

        // array to keep strong references to allocated objects:
        final int count = 10000; // 10000 or so is enough for small ojects
        Object[] objects = new Object[count];

        long heap1 = 0;

        // allocate count+1 objects, discard the first one:
        for (int i = -1; i < count; ++i) {
          Object object;

    //// INSTANTIATE YOUR DATA HERE AND ASSIGN IT TO 'object':


          object=YOUR OBJECT;
    ////end your code here
          if (i >= 0) {
            objects[i] = object;
          }
          else {
            object = null; // discard the "warmup" object
            runGC();
            heap1 = usedMemory(); // take a "before" heap snapshot
          }
        }

        runGC();
        long heap2 = usedMemory(); // take an "after" heap snapshot:

        final int size = Math.round(((float)(heap2 - heap1)) / count);
        System.out.println("'before' heap: " + heap1 +
                           ", 'after' heap: " + heap2);
        System.out.println("heap delta: " + (heap2 - heap1) +
                           ", {" + objects[0].getClass() + "} size = " + size + " bytes");
      }

      // a helper method for creating Strings of desired length
      // and avoiding getting tricked by String interning:
      public static String createString(final int length) {
        final char[] result = new char[length];
        for (int i = 0; i < length; ++i) {
          result[i] = (char)i;
        }

        return new String(result);
      }

      // this is our way of requesting garbage collection to be run:
      // [how aggressive it is depends on the JVM to a large degree, but
      // it is almost always better than a single Runtime.gc() call]
      private static void runGC()
          throws Exception {
        // for whatever reason it helps to call Runtime.gc()
        // using several method calls:
        for (int r = 0; r < 4; ++r) {
          _runGC();
        }
      }

      private static void _runGC()
          throws Exception {
        long usedMem1 = usedMemory(), usedMem2 = Long.MAX_VALUE;

        for (int i = 0; (usedMem1 < usedMem2) && (i < 1000); ++i) {
          s_runtime.runFinalization();
          s_runtime.gc();
          Thread.currentThread().yield();

          usedMem2 = usedMem1;
          usedMem1 = usedMemory();
        }
      }

      private static long usedMemory() {
        return s_runtime.totalMemory() - s_runtime.freeMemory();
      }

      private static final Runtime s_runtime = Runtime.getRuntime();

    } // end of class