史上最详细java对象占几个字节?
一、下载工具
要想分析某个对象大小,要借助一个工具jol-core,他的maven地址是:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
二、测试
首先新建一个空类,里面不包含任何字段。然后使用jol-core打印一下对象布局。
public class Dog {
}
public class App
{
public static void main( String[] args )
{
Dog dog =new Dog();
System.out.println(ClassLayout.parseInstance(dog).toPrintable());
}
}
他将输出如下信息,在最后,有一条信息为"Instance size: 16 bytes",也就是说这个对象是16个字节。
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 44 c1 00 f8 (01000100 11000001 00000000 11111000) (-134168252)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
三、对象内存布局
要说这16个字节哪来的,就得先了解java对象的内存布局,也就是如下图。
对象的内存布局分为对象头、对象中实际数据、对其填充。
先不讨论这三个干什么用,只需要知道他们分别占几个字节就行了,毕竟本文只说对象大小。
3.1 对象头大小
第一部分是对象的脑袋,分为Mark Word和Class Pointer,在不同位数的JVM有不同的大小。
在32位系统上,Mark Word是4字节,Class Pointer也是4字节,总共是8字节。
在64位系统上,受指针压缩影响,开启指针压缩时Mark Word是8字节,Class Pointer是4字节,总共12字节。关闭指针压缩Mark Word是8字节,Class Pointer也是8字节。
3.2 指针压缩
在64位上有一个指针压缩的概念,参数为XX:+UseCompressedOops,默认是开启的,开启后Class Pointer将被压缩为4字节,对象头最终共为12个字节。
64位JVM测试
开启指针压缩时输出(默认就是开启),不用管,最终大小为16字节。
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 44 c1 00 f8 (01000100 11000001 00000000 11111000) (-134168252)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
接着测试关闭指针压缩,在IDEA中点击运行配置,VM options中输入-XX:-UseCompressedOops,代表关闭指针压缩。
结果输出如下
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e0 e4 e0 94 (11100000 11100100 11100000 10010100) (-1797200672)
12 4 (object header) 07 7f 00 00 (00000111 01111111 00000000 00000000) (32519)
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
你会发现最终大小都是16字节,但是开启压缩时有一行 (loss due to the next object alignment)
大小为4字节,这个表示对齐填充,这是因为人家设计时就要求大小为8的倍数,原本12不是8的倍数,所以自己又填充了4。对齐填充没有其他意义,只为了数据对齐,保证是8的倍数。
32位JVM测试
需要下载一个32为jdk。然后快捷键Ctrl+Alt+Shift+S打开项目配置,选择32位的jdk路径。
结果为8个字节,也不需要数据对其。
com.hxl.entity.Dog object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) e0 c5 cd a3 (11100000 11000101 11001101 10100011) (-1546795552)
Instance size: 8 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
3.3 实际数据
在Dog中增加一个整形和cha类型的字段。
public class Dog {
private int age;
private char date;
}
64位JVM测试
此时在开启指针压缩情况下输出:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 44 c1 00 f8 (01000100 11000001 00000000 11111000) (-134168252)
12 4 int Dog.age 0
16 2 char Dog.date
18 6 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 6 bytes external = 6 bytes total
因为int占4字节,char占2个字节,所以,原本的大小为12(对象头)+4(int)+2(char)=18,但是18不是8的倍数啊,所以要进行数据对齐,填了6个,18+6=24个字节。
在关闭指针压缩情况下输出:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 10 a5 23 b7 (00010000 10100101 00100011 10110111) (-1222400752)
12 4 (object header) 22 7f 00 00 (00100010 01111111 00000000 00000000) (32546)
16 4 int Dog.age 0
20 2 char Dog.date
22 2 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 2 bytes external = 2 bytes total
按照上面所说,原本大小为16(对象头大小)+4(int)+2(char)=22,还需填2个字节就是8的倍数了,所以上面输出(loss due to the next object alignment)
是2个字节。
32位JVM测试
但是这一切的一切,在32位的JVM上输出如下,只有16个字节。
32位没有指针压缩的概念,所以原本大小为8(对象头)+4(int)+2(char)=14,还要填充2字节为8的倍数。所以最终为16字节。
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 08 c6 cd a3 (00001000 11000110 11001101 10100011) (-1546795512)
8 4 int Dog.age 0
12 2 char Dog.date
14 2 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 2 bytes external = 2 bytes total
3.4 引用大小
在dog中增加name。
public class Dog {
private String name;
}
64位JVM测试
同样在关闭指针压缩时输出如下,发现引用类型占8字节。最终大小为24字节。
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) f8 34 a0 2f (11111000 00110100 10100000 00101111) (799028472)
12 4 (object header) 3c 7f 00 00 (00111100 01111111 00000000 00000000) (32572)
16 8 java.lang.String Dog.name null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
开启指针压缩则不同了。引用类型占4字节。最终大小为16字节,也就是把引用类型压缩成4字节了。
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 44 c1 00 f8 (01000100 11000001 00000000 11111000) (-134168252)
12 4 java.lang.String Dog.name null
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
32JVM测试
32为上对象头8字节,引用类型占4字节。总共加上对其填充为16字节。
com.hxl.entity.Dog object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) f8 c5 dd a3 (11111000 11000101 11011101 10100011) (-1545746952)
8 4 java.lang.String Dog.name null
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
3.5 数组类型
但是如果对象是数组,在对象头中多出一条表示数组的长度。如下图
如下代码。
Dog[] dogs ={new Dog()};
System.out.println(ClassLayout.parseInstance(dogs).toPrintable());
红色地方为数组的大小。
数组分两种,基本数据类型数组和引用数据类型数组,基本数据类型数组计算方式为 (数组个数x单个元素大小),比如int数组,每个int是4字节,如果有5个元素,则数组占20字节。
但是引用类型就不一样了,开启指针压缩情况下为(数组长度x4),每个引用4字节,关闭后,每个引用8字节,占用(数组长度x8)字节。
如下,数组长度为8,每个引用8字节,最终占64字节(关闭指针压缩)。
Dog[] dogs ={new Dog(),new Dog(),new Dog(),new Dog(),new Dog(),new Dog(),new Dog(),new Dog()};
System.out.println(ClassLayout.parseInstance(dogs).toPrintable());
[Lcom.hxl.entity.Dog; object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 20 37 a0 93 (00100000 00110111 10100000 10010011) (-1818216672)
12 4 (object header) 11 7f 00 00 (00010001 01111111 00000000 00000000) (32529)
16 4 (object header) 08 00 00 00 (00001000 00000000 00000000 00000000) (8)
20 4 (alignment/padding gap)
24 64 com.hxl.entity.Dog Dog;.<elements> N/A
Instance size: 88 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
在32位上则(数组个数x4)。如8个引用元素则是32字节。
上一篇: java gzip 压缩 解压缩
推荐阅读