这次说的是64bit的jvm,暂不谈32bit的jvm,两者的对象大小是不一样的。
java对象的存储布局分为两种情况:普通对象 和 数据。
先说普通对象吧:如下图
那为啥对齐单元的大小是8个字节呢?
因为是64bit位的jvm,8个字节刚好是64个比特位,这样读取的效率高。
那这个东西我们在代码里面看得到吗? 可以看到的。
我们在pom.xml里面引入依赖:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.14</version>
<scope>provided</scope> <dependency>
但是我的报错啊:java.lang.ClassNotFoundException: org.openjdk.jol.info.ClassLayout,我也不知道为啥,解决办法如下:
点开如下链接:
- Jar包下载地址
,选择一个版本,我选择的是0.10/这个版本,亲测可用:
然后选择格式符合: jol-cli-.-full.jar的包
然后将jar包放到项目里面,新建lib目录,放到lib目录下
然后选中jar,将其添加为依赖,并且删除原来的maven依赖。
这样就可以用了。
我们在代码里运行下:
1.0 情形:
public class Test { } public class RunTest { public static void main( String[] args ) { Test test = new Test(); System.out.println(ClassLayout.parseInstance(test).toPrintable()); } }
运行的结果如下:
奇怪了,怎么只有header和padding呢,实例数据那一部分呢?
实例数据是指成员变量,而Test.java里面没有成员变量。所以总共16个字节。
那如果是Object类型的对象呢,多少个字节?Object里面没有成员变量
1.1情形:
public class Test { int a; } public class RunTest { public static void main( String[] args ) { Test test = new Test(); System.out.println(ClassLayout.parseInstance(test).toPrintable()); } }
这种情况应该是多少个字节呢? int类型占4个字节。如下图:
header部分不变,依然是12个字节,成员变量a占4个字节,刚好16个字节。
那为什么这次没有填充数据呢?注意了,只有在对象大小不能被8整除的时候才需要填充数据。
那如果是这样呢:
public class Test { int a; int b; } 多少个字节? header部分不变12 bytes,两个int类型成员变量8 bytes,这个时候需要填充4 bytes, 加起来共24个字节。
那如果是这样呢:
public class Test { int a; int b; boolean c; } 多少个字节? header部分不变12 bytes,两个int类型成员变量8 bytes,注意了boolean类型只占1个字节,所以需要填充3个字节,凑成24个字节。
那如果是这样呢:
public class Test {
int a;
int b;
boolean c;
String str = "hello";
} 多少个字节?header部分不变12 bytes,两个int类型成员变量8 bytes,注意了boolean类型只占1个字节。字符串类型的是怎么算呢? 首先啊,字符串是一个特殊的对象,它是存放在字符串常量池中,那Test对象是多少字节呢?那str的大小为零吗 或者是5个字节吗?? str是句柄,引用类型的,是存储对象的地址,也就是普通对象的指针,这个指针占用多少个字节呢?
12 + 4 + 4 + 1 + 3 + 4 + 4 = 32 bytes。有两个问题哦:
1. 为什么编译器会将boolean类型转为int类呢? 2. 64位的计算机,指针应该是8个字节啊。64位的jvm,对象的指针为什么是4个字节呢?
首先回答问题1:
因为虚拟机规范只有 4字节 和 8字节, 大部分指令都没有支持 byte、char 和 short 类型,甚至没有任何指令支持 boolean 类型。编译器会在编译器或运行期将 byte 和 short 类型带符号扩展为 int 类型, boolean 和 char 类型零位扩展为相应的 int 类型。与之类似,在处理 boolean、byte、char 和 short 类型的数组时,也会转为使用相应的 int 类型的字节码来处理指令。 因此,大多数对于 boolean、byte、char 和 short 类型数据的操作,实际都是使用 int 类型作为运算类型。另外还有第二点原因,在设计虚拟机时,主要考虑的是 32位体系,32位系统使用 4 字节是最节省,因为 CPU 只能 32位32位的寻址。
都说 byte、boolen 类型占 1字节,但上面又提到, byte 会被提升为 int 类型,那么就应该占了 4字节类型(long、float), boolean、char、short 都是占了 4字节。
再来回答问题2:
64bit的jvm,寻址能力也就是寻址范围是2的64次方啊,所以指针应该占用8个字节啊。但是jdk里面默认两个参数都开启了,一个是class对象的指针压缩,另一个是普通对象的指针压缩:
如下图,执行命令:java -XX:+PrintCommandLineFlags -version
我们可以配置关闭这两个压缩:
将 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops 改为: -XX:-UseCompressedClassPointers -XX:-UseCompressedOops
关闭压缩后,再运行,就成了 32(关闭前) + 4 + 4 = 40bytes,如下图:
本次就分享这么多,欢迎大佬们指正!