一种Java中对象实例占用堆空间衡量方法

<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">&#160;<!-- 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="宋体">&#160;&#160; </font><font face="微软雅黑">private static Instrumentation instance; </font></font></p> <p><font color="#0080c0"><font face="宋体">&#160;&#160; </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="宋体">&#160;&#160; </font><font face="微软雅黑">{ </font></font></p> <p><font color="#0080c0"><font face="宋体">&#160;&#160;&#160;&#160;&#160; </font><font face="微软雅黑">InstrumentationUtil.instance = instantce; </font></font></p> <p><font color="#0080c0"><font face="宋体">&#160;&#160; </font><font face="微软雅黑">} </font></font></p> <p><font color="#0080c0"><font face="宋体">&#160;&#160; </font><font face="微软雅黑">public static void permain(String args)</font></font></p> <p><font color="#0080c0"><font face="宋体">&#160;&#160; </font><font face="微软雅黑">{ </font></font></p> <p><font color="#0080c0"><font face="宋体">&#160;&#160; </font><font face="微软雅黑">} </font></font></p> <p><font color="#0080c0"><font face="宋体">&#160;&#160; </font><font face="微软雅黑">public static Instrumentation getInstance() </font></font></p> <p><font color="#0080c0"><font face="宋体">&#160;&#160; </font><font face="微软雅黑">{ </font></font></p> <p><font color="#0080c0"><font face="宋体">&#160;&#160;&#160;&#160;&#160; </font><font face="微软雅黑">return instance; </font></font></p> <p><font color="#0080c0"><font face="宋体">&#160;&#160; </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>&#160; <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(&quot;A Object with one reference is : &quot;, obj1); </font></p> <p><font color="#0080c0" face="微软雅黑">simpleMeasure(&quot;A Object with two reference is : &quot;, obj2);</font></p> <p><font color="#0080c0" face="微软雅黑">simpleMeasure(&quot;A Object with three reference is : &quot;, 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="微软雅黑">&#160;&#160; 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="微软雅黑">&#160;&#160; private Object obj1; </font></p> <p><font color="#0080c0" face="微软雅黑">&#160;&#160; 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="微软雅黑">&#160;&#160; private Object obj1; </font></p> <p><font color="#0080c0" face="微软雅黑">&#160;&#160; private Object obj2; </font></p> <p><font color="#0080c0" face="微软雅黑">&#160;&#160; 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>结合1、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(&lt;int&gt;4) + charLength*2。运行和和猜想结果一致。</p> <blockquote> <p><font color="#0080c0" face="微软雅黑">private static void basicType() <br />&#160;&#160;&#160; { <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //object 8 + length 4 + 2 <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; char[] buffer1 = new char[1];//16 <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; char[] buffer2 = new char[2];//16 <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; char[] buffer3 = new char[3];//24 <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; char[] buffer4 = new char[4];//24 <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; char[] buffer5 = new char[5];//24 <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; char[] buffer6 = new char[6];//24 <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; char[] buffer7 = new char[7];//32 <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; simpleMeasure(&quot;A 1 size char buffer value is : &quot;, buffer1); <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; simpleMeasure(&quot;A 2 size char buffer value is : &quot;, buffer2); <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; simpleMeasure(&quot;A 3 size char buffer value is : &quot;, buffer3); <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; simpleMeasure(&quot;A 4 size char buffer value is : &quot;, buffer4); <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; simpleMeasure(&quot;A 5 size char buffer value is : &quot;, buffer5); <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; simpleMeasure(&quot;A 6 size char buffer value is : &quot;, buffer6); <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; simpleMeasure(&quot;A 7 size char buffer value is : &quot;, buffer7); <br />&#160;&#160;&#160; }</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对象的空间占用是递归计算的,即对象类型本身-&gt;父类-&gt;祖先类-&gt;Object[end]。</li> </ul>

转载于:https://my.oschina.net/pangyangyang/blog/120047

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值