你知道資料大小嗎?--不要花太多的功夫來隱藏類的成員(一) (轉)

amyz發表於2007-08-15
你知道資料大小嗎?--不要花太多的功夫來隱藏類的成員(一) (轉)[@more@] 

你知道資料大小嗎?:namespace prefix = o ns = "urn:schemas--com::office" />

--不要花太多的功夫來隱藏類的成員

摘要:

過去很多年裡面,許多的開發人員都一直在問一個問題:“一個Java到底耗費多少呢?”在本文中,Vladimir Roubtsov用以前的解決方案來解釋了這個問題,在此之外,基於他的演示了記憶體的使用,並且還提供了一些技巧來讓你的Java更加高效。

作者:Vladimir Roubtsov

近來,我們幫助開發了一個Java,這是一個類似記憶體的應用。那就是說,我們特別強調設計,因為設計要特別考慮在記憶體中快取大量的資料來提高查詢的。

一旦我們得到執行的原型,那麼在資料從上宰入經過分析以後,我們自然的決定了資料的輪廓。不太滿意的初始效果,促使我們尋找更好的解決方案。

工具:

既然Java有目的的隱藏了很多記憶體管理的細節資訊,要發現你的物件要消耗多少記憶體的確要花一些功夫。你可以使用Runtime.freeMemory()方法來測量在一個或者多個物件被分配前前後的堆的變化值。有一些文章,例如(Ramchander Varadarajan's "/jw-javatip130.html#res#resources">Question of the Week No. 107" (Sun Microsystems, September 2000) and Tony Sintes's "" (JavaWorld, December 2001))都詳細地介紹了這些方法。但是不幸的是,這些先前的文章的解決方案的失敗在於實現中使用了一個錯誤的Runtime方法。即使後來的文章也有它的不完整性。

l  Runtime.freeMemory() 方法提供的功能是不夠的,因為JVM可以在任何時候(只要需要,特別是在執行垃圾收集的時候)決定增加它的當前的堆大小。除非在執行的時候已經使用了引數-Xmx指定了堆的最大值,否則我們應該使用Runtime.totalMemory()-Runtime.freeMemory()作為在使用的堆大小。

l  單個Runtime.gc()方法並不能保證有效的請求垃圾收集。舉例來說,我們可以請求物件的finalizer執行正常。既然Runtime.gc()不能保證阻塞到垃圾處理,那麼一直等待到當堆大小穩定以後是一個很好的辦法。

l  如果輪廓類建立了一個靜態的資料作為先前的類初始化的一部分,那麼堆記憶體對於第一個類例項的分配的空間應該包括這個資料。我們應該忽略被第一個類例項消耗的堆空間。

考慮這些問題:我們給出了一個Sizeof,作為一個工具來檢視各種Java核心和應用類。

public class Sizeof

{

  public static void main (String [] args) throws Exception

  {

  // Waup all classes/methods we will use

  runGC ();

  usedMemory ();

  // Array to keep strong references to allocated s

  final int count = 100000;

  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 = null;

 

  // Instantiate your data here and assign it to object

 

  object = new Object ();

  //object = new Integer (i);

  //object = new Long (i);

  //object = new String ();

  //object = new byte [128][1]

 

  if (i >= 0)

  objects [i] = object;

  else

  {

  object = null; // Discard the warm up 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");

  for (int i = 0; i < count; ++ i) objects [i] = null;

  objects = null;

  }

  private static void runGC () throws Exception

  {

  // 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 < 500); ++ 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 

Sizeof的關鍵方法是runGC() 和usedMemory()方法,我使用了runGC()這樣的封裝方法來呼叫_runGC()幾次,是為了讓這個方法有更加明顯的效果。

注意我呼叫runGC()方法的地方,你可以在heap1和heap2編輯你的程式碼,加入你任何你感興趣的例子。

也請注意Sizeof怎麼樣輸出物件的大小,資料的傳遞閉包要求被用所得count類例項所用到,然後被count整除。對於大多數類來說,這個結果將是單個類例項物件所耗費的記憶體大小,包括所有它自己的成員域。記憶體的界限值不同於由一些商業的工具報告的影子記憶體界限(比如說,如果一個物件有一個int[],那麼它的記憶體消耗將顯得很特別)。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-957579/,如需轉載,請註明出處,否則將追究法律責任。

相關文章