<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