OOP模型、计算java对象大小
OOP模型
oop是java对象在jvm中的存在形式。
一个oop对象分为 对象头区域,实例数据区域,对其填充这三部分。
对象头
对象头区域分为三部分,Mark Word,类型指针,数组长度。
-
Mark Word:标记字。主要用来表示对象的线程锁状态,存放对象hashCode、GC次数
32bit占用:4B
64bit占用:8B
-
类型指针(Klass pointer):指向Class信息的指针,表示该对象是哪个Class的实例
开启指针压缩:4B
关闭指针压缩:8B
-
数组长度,当对象不是数组对象时,该区域不占空间。
默认占用4B
实例数据
类的非静态属性,生成的数据就是对象的实例数据
byte:1B
short:2B
int:4B
long:8B
double:8B
float:4B
char:2B
boolean:1B
ref:
32位 4字节
64位 8字节
64位如果使用指针压缩就是 4字节
对象填充区域
默认8字节对齐。当一个对象的大小不足8的整数倍的时候。会填充字节。
假如一个对象是30字节,会默认填充2个字节,达到8字节对齐。
计算对象大小
以下测试都是在开启指针压缩下测试,单位是字节byte
使用jol验证计算正确
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
普通对象
MarkWord KlassPointer 数组长度 实例数据 对齐填充
8 + 4 + 0 + 0 +4 =16
public class TestOOP {
public static void main(String[] args) {
TestOOP testOOP = new TestOOP();
System.out.println(ClassLayout.parseInstance(testOOP).toPrintable());
}
}
带属性对象
静态变量不占用oop的内存空间
MarkWord KlassPointer 数组长度 实例数据 对齐填充
8 + 4 + 0 + 4+ 4+4 +0 =24
public class TestOOP {
static int i = 0;
int b = 1;
int c = 1;
String a = "1";
public static void main(String[] args) {
TestOOP testOOP = new TestOOP();
System.out.println(ClassLayout.parseInstance(testOOP).toPrintable());
}
}
String对象
MarkWord KlassPointer 数组长度 实例数据 对齐填充
String对象 8 + 4 + 0 + 4+ 4+4 +0 =24
字符数组 8 + 4 + 4 + 2+ 2+2+2+2 +6 =32
所以"hello"占用58个字节
计算公式 24(String对象)+ (16+字符串长度*2+填充)
System.out.println(ClassLayout.parseInstance("hello").toPrintable());
System.out.println(GraphLayout.parseInstance("hello").toPrintable());
ArrayList对象
ArrayList底层用的 Object[] elementData 数组维护的数据。
MarkWord KlassPointer 数组长度 实例数据 对齐填充
ArrayList 8 + 4 + 0 + 4+ 4+4 +0 =24
elementData[] 8 + 4 + 4 + 4*10(数组占用空间) +0=56
ArrayList在不存储数据的情况下elementData[]不占用空间,只要add一个元素,数组长度就最少为10,所以占用80个字节,扩容情况下另行计算
“1” 字符串占24+(16+2+6)=48
Integer类型占用的空间 16
占用24 +56 + 48 +16 =144
ArrayList<Object> list = new ArrayList<Object>();
list.add("1");
list.add(1);
System.out.println(ClassLayout.parseInstance(list).toPrintable());
System.out.println(GraphLayout.parseInstance(list).toPrintable());
System.out.println(GraphLayout.parseInstance(list).totalSize());
HashMap对象
HashMap用的是Node[] 数组维护的数据。
Node对象中有四个属性,其中一个int类型。三个引用类型
MarkWord KlassPointer 数组长度 实例数据 对齐填充
HashMap 8 + 4 + 0 + 4*8 +4 =48
Node[] 8 + 4 + 4 + 4*16 +0 =80
HashMap在不存储数据的情况下Node[] 不占用空间,只要put一个元素,数组长度就最少为16,所以占用128个字节,扩容情况下另行计算
Node[0] 8 + 4 + 0 + 4*4 +4 =32
Node[1] 8 + 4 + 0 + 4*4 +4 =32
加上字符串的占用空间 48+48 两个"1" 会在字符串常量池,只记录一个在内存
Integer类型占用的空间 16
总共 128+ 32 + 32 + 48 +48 +16 =304
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("1","1");
map.put("2",2);
System.out.println(ClassLayout.parseInstance(map).toPrintable());
System.out.println(GraphLayout.parseInstance(map).toPrintable());
System.out.println(GraphLayout.parseInstance(map).totalSize());
上一篇: Java对象大小的计算方式