最近在学习java对象内存布局方面的一些知识,主要是想知道一个java对象到底占用多少内存空间,以及java对象在内存中到底是什么样子的。c/c++中的sizeof运算符能够方便地告诉我们一个变量占用的内存空间,但是在java中却没有直接提供这种机制。如果想获取java对象占用的内存大小,可以利用java的Instrumentation机制。java.lang.instrument.Instrumentation这个接口提供了getObjectSize(Object
objectToSize),能够告诉我们一个对象的大小。如何获取Instrumentation对象,网上有很多资料,这里不再赘述。
public static void main(String[] args) { // 通过pre-agent获取Instrumentation对象 Instrumentation instr = AgentGetter.getInstrumentation(); System.out.println(instr.getObjectSize(new Object())); }
Java对象的内存布局:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。无论是32位还是64位的HotSpot,使用的都是8字节对齐。也就是说每个java对象,占用的字节数都是8的整数倍。(对象头 + 实例数据 + padding) % 8等于0且0 <= padding < 8。在网上看到各种介绍如何手动计算对象大小的文章,总结了几点:
1.基本数据类型占用的字节数,JVM规范中有明确的规定,无论是在32位还是64位的虚拟机,占用的内存大小是相同的。
2.reference类型在32位JVM下占用4个字节,但是在64位下可能占用4个字节或8个字节,这取决于是否启用了64位JVM的指针压缩参数UseCompressedOops。
3.new Object()这个对象在32位JVM上占8个字节,在64位JVM上占16个字节。
4.开启(-XX:+UseCompressedOops)指针压缩,对象头占12字节; 关闭(-XX:-UseCompressedOops)指针压缩,对象头占16字节。
5.64位JVM上,数组对象的对象头占用24个字节,启用压缩之后占用16个字节。之所以比普通对象占用内存多是因为需要额外的空间存储数组的长度。
6.对象内存布局中的实例数据,不包括类的static字段的大小,因为static字段是属于类的,被该类的所有对象共享。
更具体的可以参考“一个Java对象到底占用多大内存?”这篇文章。在网上搜索的过程中看到了java object layout这个小工具,能够打印出类的布局信息。
项目的主页是:http://openjdk.java.net/projects/code-tools/jol/,
项目代码在http://central.maven.org/maven2/org/openjdk/jol/。
下载jol-core-0.3.2.jar之后,将其加入项目的build path,通过下面的代码就可以查看某个类的布局信息。
package jol; import org.openjdk.jol.info.ClassLayout; import org.openjdk.jol.util.VMSupport; public class T1 { public static void main(String[] args) throws Exception { System.out.println(VMSupport.vmDetails()); System.out.println(ClassLayout.parseClass(VO.class).toPrintable()); } }
运行这段代码,可以看到jol输出如下信息:
Running 64-bit HotSpot VM. Objects are 8 bytes aligned. Field sizes by type: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] Array element sizes: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] jol.VO object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 16 (object header) N/A 16 8 long VO.b N/A 24 4 int VO.a N/A 28 4 (loss due to the next object alignment) Instance size: 32 bytes (estimated, the sample instance is not available) Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
可以看到jol工具,能够显示出对象头的大小,以及每个实例字段的偏移,进而计算对象占用的内存大小。
jol工具还提供了一个命令行工具jol-cli-0.3.2-full.jar,包含了main方法能够直接在命令行运行,查看类的布局信息。可以从这里下载jol-cli工具。
#查看jdk系统类的布局信息 >java -jar jol-cli-0.3.2-full.jar internals java.lang.Object
#如果我们自己的jar放在C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\下,那么会自动加载; #如果我们自己的jar放在C:\Program Files\Java\jdk1.7.0_80\jre\lib\下,那么不会自动加载(可能是因为安全问题#,bootstrap类加载器仅仅加载jdk的核心类); >java -jar jol-cli-0.3.2-full.jar internals net.aty.VO
#方式1:指定classpath,查看自己的类布局 >java -jar jol-cli-0.3.2-full.jar internals -cp mydemo.jar net.aty.VO
#方式2:指定classpath,查看自己的类布局 >java -cp mydemo.jar;jol-cli-0.3.2-full.jar org.openjdk.jol.Main internals net.aty.VO
上面几种方式都能够正确运行,但是下面这种方式是错误的,会报错ClassNotFoundException。
#注意:这种方式会报错:java.lang.ClassNotFoundException: #这是因为:如果采用了-jar参数来运行可执行的jar包,JVM会忽略你设置的classpath以及所有环境变量 java -cp mydemo.jar -jar jol-cli-0.3.2-full.jar internals net.aty.VO