<p><em><font face="宋體">本文的方法是利用java.lang.Instrumentation的工具類來實現的,以前看過一篇紅薯的文章介紹了這方面的內容就跟着作了下,並總結出一套比較靠譜的推算方式。紅薯的文章由於是當時看的沒有記錄,暫時沒有找到了,同時本文參考了一篇18摸的文章連接以下:</font></em><a href="http://www.ibm.com/developerworks/cn/java/j-lo-jse61/ "><em><font face="宋體">Java SE 6 新特性: Instrumentation 新功能</font></em></a></p> <p><font face="宋體"><strong>前置條件:</strong></font></p> <font face="宋體"><strong> <p>1,本方法只在JDK6上驗證過,Instrumentation接口是在JDK1.5引入的,因此1.5可能可行,1.5之前的版本就不可行了。 </p> </strong> <p><strong>2,本位驗證環境爲32系統,64位系統不涉及。</strong> </p> <p><strong>3,本文須要經過打包爲jar文件來實現,對jar的存在以下要求:</strong></p> <p><font size="2">3.1 jar文件中須要有一個類實現了public static void premain(java.lang.String args, java.lang.instrument.Instrumentation instantce)方法,這個方法是用來傳入Instrumentation接口的實現,由於這個接口的實現是native code的,在jdk中自己並無固定實現,我的猜測是不一樣jvm各自實現的。</font></p> <p><font size="2">3.2 須要編輯jar文件的MANIFEAST.MF文件,添加屬性Premain-Class,這個屬性對應的值也就是3.1中實現對應方法的類全路徑。</font></p> <p><font size="2">3.3 打包後在命令行裏執行同時須要提供javaagent參數。運行命令以下java -javaagent:[jar路徑] [包含main函數的類]。以個人實現爲例:</font></p> <blockquote> <p><font size="2"> <!-- lang: shell --><font color="#0080c0">java -javaagent:test.jar com.willard.instrumentation.object.size.ObjectMeasure</font></font></p> </blockquote> <p><font color="#808080" size="2"><font color="#000000">我用於獲取Instrumetation的類實現(做爲一個Util類用):</font> </font></p> <blockquote> <p><font face="微軟雅黑"><!-- lang: java --><font color="#0080c0">public class InstrumentationUtil</font></font></p> <p><font color="#0080c0" face="微軟雅黑">{ </font></p> <p><font color="#0080c0"><font face="宋體">   </font><font face="微軟雅黑">private static Instrumentation instance; </font></font></p> <p><font color="#0080c0"><font face="宋體">   </font><font face="微軟雅黑">public static void premain(java.lang.String args, java.lang.instrument.Instrumentation instantce) </font></font></p> <p><font color="#0080c0"><font face="宋體">   </font><font face="微軟雅黑">{ </font></font></p> <p><font color="#0080c0"><font face="宋體">      </font><font face="微軟雅黑">InstrumentationUtil.instance = instantce; </font></font></p> <p><font color="#0080c0"><font face="宋體">   </font><font face="微軟雅黑">} </font></font></p> <p><font color="#0080c0"><font face="宋體">   </font><font face="微軟雅黑">public static void permain(String args)</font></font></p> <p><font color="#0080c0"><font face="宋體">   </font><font face="微軟雅黑">{ </font></font></p> <p><font color="#0080c0"><font face="宋體">   </font><font face="微軟雅黑">} </font></font></p> <p><font color="#0080c0"><font face="宋體">   </font><font face="微軟雅黑">public static Instrumentation getInstance() </font></font></p> <p><font color="#0080c0"><font face="宋體">   </font><font face="微軟雅黑">{ </font></font></p> <p><font color="#0080c0"><font face="宋體">      </font><font face="微軟雅黑">return instance; </font></font></p> <p><font color="#0080c0"><font face="宋體">   </font><font face="微軟雅黑">} </font></font></p> <p><font color="#0080c0" face="微軟雅黑">} </font></p> </blockquote> <p>jar中MANIFEAST.MF文件內容以下: `</p> <blockquote> <p><font color="#0080c0">Manifest-Version: 1.0` <br />`Premain-Class: com.willard.instrumentation.InstrumentationUtil`</font> </p> </blockquote> <p><strong>一些用來計算的規則:</strong></p> <p>1,一個Object對象佔用8個字節(Byte),這個是固定的——讀到並驗證了,至於出處確實不詳。 <br />2,一個對象的大小必須是8的倍數,不足8的倍數時會入到8的倍數上。這個一樣是驗證了可參考下面的論述。 <br />3,一個引用reference佔用的是4個字節(Byte)。這個是推算出來的,代碼以下:</p> <blockquote> <p>  <br /><!-- lang: java --><font color="#0080c0" face="微軟雅黑">private static void mesureReference()</font></p> </blockquote> <blockquote> <p><font color="#0080c0" face="微軟雅黑">{ </font></p> </blockquote> <blockquote> <p><font color="#0080c0" face="微軟雅黑">Object obj1 = new RefA(); </font></p> <p><font color="#0080c0" face="微軟雅黑">Object obj2 = new RefB(); </font></p> <p><font color="#0080c0" face="微軟雅黑">Object obj3 = new RefC(); </font></p> <p><font color="#0080c0" face="微軟雅黑">simpleMeasure("A Object with one reference is : ", obj1); </font></p> <p><font color="#0080c0" face="微軟雅黑">simpleMeasure("A Object with two reference is : ", obj2);</font></p> <p><font color="#0080c0" face="微軟雅黑">simpleMeasure("A Object with three reference is : ", obj3); } </font></p> </blockquote> <blockquote> <p><font color="#0080c0" face="微軟雅黑">private static class RefA</font></p> <p><font color="#0080c0" face="微軟雅黑">{ </font></p> <p><font color="#0080c0" face="微軟雅黑">   private Object obj1; </font></p> <p><font color="#0080c0" face="微軟雅黑">} </font></p> <p><font color="#0080c0" face="微軟雅黑">private static class RefB </font></p> <p><font color="#0080c0" face="微軟雅黑">{</font></p> <p><font color="#0080c0" face="微軟雅黑">   private Object obj1; </font></p> <p><font color="#0080c0" face="微軟雅黑">   private Object obj2; </font></p> <p><font color="#0080c0" face="微軟雅黑">} </font></p> <p><font color="#0080c0" face="微軟雅黑">private static class RefC </font></p> <p><font color="#0080c0" face="微軟雅黑">{ </font></p> <p><font color="#0080c0" face="微軟雅黑">   private Object obj1; </font></p> <p><font color="#0080c0" face="微軟雅黑">   private Object obj2; </font></p> <p><font color="#0080c0" face="微軟雅黑">   private Object obj3; </font></p> <p><font color="#0080c0" face="微軟雅黑">} </font></p> <p><font color="#0080c0" face="微軟雅黑">}</font></p> </blockquote> <p>這裏打印出來的結果以下: </p> <p>A Object with one reference is : 16 <br />A Object with two reference is : 16 <br />A Object with three reference is : 24 <br /></p> <p>結合一、2條規則能夠推出,首先除了基本對象任何對象都是Object(8 Byte), 一個類包含一個和兩個引用時都是16 Byte說明一個reference小於8 Byte,同時驗證了第二條。當一個類包含三個引用時變成了24,由此得出一個reference佔用4Byte。 <br /></p> <p>4,基本變量所佔用的對空間統計以下:</p> <p><em>統計方法比較笨,是聲明一組class,這組class分別包括1~8個同類型的變量,類型就是下表一次列出的,而後查看在變量數遞增是這個class的實例體積變化。</em></p> <p><em>起初想直接嘗試測量一個int等類型對象的大小,結果返回的都是16.緣由應該是java的自動封裝特性,由於Instrumentation.getObjectSize(Object obj)接受的是Object類型的參數,因此java自動轉換類型爲封裝類型如Integer。經過查看下面類型的封裝類能夠發現都是同樣的——繼承自Object,擁有一個私有變量value,由此也就解釋了爲何。</em></p> <p> <table border="1" cellspacing="0" cellpadding="2" width="400"><tbody> <tr> <td valign="top" width="199">boolean</td> <td valign="top" width="199">1</td> </tr> <tr> <td valign="top" width="199">short</td> <td valign="top" width="199">2</td> </tr> <tr> <td valign="top" width="199">char</td> <td valign="top" width="199">2</td> </tr> <tr> <td valign="top" width="199">char_zh</td> <td valign="top" width="199">2</td> </tr> <tr> <td valign="top" width="199">int</td> <td valign="top" width="199">4</td> </tr> <tr> <td valign="top" width="199">float</td> <td valign="top" width="199">4</td> </tr> <tr> <td valign="top" width="199">long</td> <td valign="top" width="199">8</td> </tr> <tr> <td valign="top" width="199">double</td> <td valign="top" width="199">8</td> </tr> </tbody></table> </p> <p>5,String對象的計算,String顯示的值是24。經過String的類定義能夠看出來(下圖),一個char[]和3個int對象。在java中除了基本類型一切都是對象,因此char[]自己也是對象那麼就是個引用所以String大小計算:Object(8) + char[](4) + int*3(12) = 24。所以在計算String大小的時候就應該是24+char[]實際的佔用量。</p> <p><a href="http://static.oschina.net/uploads/img/201304/06005853_fZh3.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="String" border="0" alt="String" src="http://static.oschina.net/uploads/img/201304/06005854_ayx0.png" width="543" height="206" /></a> </p> </font> <p>6,char[]的計算使用了下面一段代碼做爲實驗,首先Java中一切皆對象,因此推測數組=Object(8) + length(<int>4) + charLength*2。運行和和猜測結果一致。</p> <blockquote> <p><font color="#0080c0" face="微軟雅黑">private static void basicType() <br />    { <br />        //object 8 + length 4 + 2 <br />        char[] buffer1 = new char[1];//16 <br />        char[] buffer2 = new char[2];//16 <br />        char[] buffer3 = new char[3];//24 <br />        char[] buffer4 = new char[4];//24 <br />        char[] buffer5 = new char[5];//24 <br />        char[] buffer6 = new char[6];//24 <br />        char[] buffer7 = new char[7];//32 <br />        simpleMeasure("A 1 size char buffer value is : ", buffer1); <br />        simpleMeasure("A 2 size char buffer value is : ", buffer2); <br />        simpleMeasure("A 3 size char buffer value is : ", buffer3); <br />        simpleMeasure("A 4 size char buffer value is : ", buffer4); <br />        simpleMeasure("A 5 size char buffer value is : ", buffer5); <br />        simpleMeasure("A 6 size char buffer value is : ", buffer6); <br />        simpleMeasure("A 7 size char buffer value is : ", buffer7); <br />    }</font></p> </blockquote> <p>最後總結一下</p> <ul> <li>一個Java對象首先是一個Object,所以8個字節已佔據。</li> <li>基本類型的變量(field)佔用空間能夠從表中查出,這些類型是傳值的沒有引用的開銷。</li> <li>非基本類型的變量除了變量自己佔用還要計算至少一個引用的開銷,若是Java對象中多個引用指向一個對象就要計算屢次引用。</li> <li>數組也是對象,包括Object的8個字節+length變量的四個字節,以及根據數組類型計算的內存佔用,若是是非基本類型的數組還要計算引用的開銷length * 4(已驗證,略過)。</li> <li>一個複雜的Java對象的空間佔用是遞歸計算的,即對象類型自己->父類->祖先類->Object[end]。</li> </ul>java