2009-04-16 45 views
16

我需要以編程方式找出給定的Java對象佔用的內存量,包括它所引用的對象佔用的內存。以編程方式計算Java對象佔用的內存,包括它引用的對象

我可以生成內存堆轉儲並使用工具分析結果。但是,生成堆轉儲需要相當長的時間,並且需要使用此工具來讀取轉儲以生成報告。考慮到我可能需要多次這樣做,如果我可以在我的項目中添加一些代碼,使得我的值爲「運行時」,我的工作就會更加敏捷。

我怎麼能最好地實現這一目標?

PS:我特別有型javax.xml.transform.Templates

+0

我wouldnt稱這是一個確切的愚蠢。 – Inisheer 2009-04-16 18:10:41

+0

嗯,不完全是一個騙局,因爲原來的問題未能提供一個在這方面可用的答案... – Varkhan 2009-04-16 18:11:03

+0

它提供了幾個。 – erickson 2009-04-16 18:46:34

回答

11

的對象的集合,您將需要使用反射了點。得到的一段代碼過於複雜,我在這裏發表(儘管它很快將作爲GPL工具我建立的一部分),但其主要思想是:

  • 對象頭使用8個字節(對於類指針和引用計數)
  • 每個基本字段使用1,2,4或8個字節,具體取決於實際類型
  • 每個對象引用(即非原語)字段使用4個字節(引用,加上任何參考對象使用)

您需要分別對待數組(8個頭的tes,4字節的長度字段,4 *長度字節的表,加上任何內部使用的對象)。您需要使用反射來處理其他類型的對象迭代通過字段(以及它的父字段)。

您還需要在遞歸期間保留一組「已見」對象,以免在多處引用多次對象。

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 
6

看起來好像已經有一個工具可以做到這一點,稱爲Classmexer

我自己並沒有嘗試過,但是在我翻身之前我會走那條路。