引用
需要的工具
<!--用来专门查看对象的内存布局情况-->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
<!-- 可以告诉我们数据项分配的字节数大小 -->
<dependency>
<groupId>com.carrotsearch</groupId>
<artifactId>java-sizeof</artifactId>
<version>0.0.5</version>
</dependency>
对象计算
64位JVM默认会开启指针压缩,开启后对象头占用12个字节
对象分为 对象头 + 实例数据 + 内存对齐 3部分
1.对象头
而对象头又分为三部分:
a. Mark Word 8字节
用来标记对象状态的,比如hashCode、锁状态信息等
b. 指向类的指针 8字节 开启指针压缩后 4字节
大小也通常为32 bit, 它主要指向类的数据,也就是指向方法区中的位置
c. 数组长度 4字节
在32位或者64位JVM中,长度都是32 bit 。数组对象才具有大小
2.实例数据
指的是对象中的成员变量,如果是简单类型,则直接存对象本身,如果是非简单类型,则保存对象引用
3.内存对齐
对于64位Java虚拟机来说,对象为8字节对齐,如果对象大小不满8字节的倍数,则通过该部分进行补齐
举例
普通对象
public static void main(String[] args) {
Object obj = new Object();
System.out.println("Obj:" + ClassLayout.parseInstance(obj).toPrintable());
}
结果如下:
对象实类大小 = 对象头 + 实例数据 + 内存对齐
= 12B(8B mark word + 4B指向 ) + 0B实例数据 + 4B对象填充(为8倍数)
= 16B
有实例
public class Ref {
private int anInt;
private byte aByte;
}
public static void main(String[] args) {
Ref ref = new Ref();
ref.setaByte((byte) 127);
ref.setAnInt(127);
System.out.println("Obj:" + ClassLayout.parseInstance(ref).toPrintable());
}
结果如下:
对象实类大小 = 对象头 + 实例数据 + 内存对齐 =
12B(8B mark word + 4B指向 ) + 4B(int) + 1B(byte) + 7B对象填充(为8倍数)
= 24B
增加数组对象
public class Ref {
private int anInt;
private byte aByte;
}
public static void main(String[] args) {
Ref[] abc = new Ref[]{};
System.out.println("Obj:" + ClassLayout.parseInstance(abc).toPrintable());
}
结果如下:
可以看到增加了数组对象
对象实类大小 = 对象头 + 实例数据 + 内存对齐 =
16B(8B mark word + 4B指向 + 4B 数组对象 ) +0B 实例数据 + 0B 对象填充
= 16B
再增加数据
public class Ref {
private int anInt;
private byte aByte;
}
public static void main(String[] args) {
Ref ref01 = new Ref();
ref01.setAnInt(12);
Ref ref02 = new Ref();
ref01.setAnInt(13);
Ref[] abc = new Ref[]{ref01,ref02};
Ref[] abc = new Ref[]{ref};
System.out.println("Obj:" + ClassLayout.parseInstance(abc).toPrintable());
}
可以看到在数组对象的基础上增加了实例数据 多了8b
对象实类大小 = 对象头 + 实例数据 + 内存对齐 =
16B(8B mark word + 4B指向 + 4B 数组对象 ) +8B 实例数据 + 0B 对象填充
= 24B
关闭指针压缩手动添加命令
类指针压缩关闭参数为:-XX:-UseCompressedClassPointers
普通对象指针压缩关闭参数为: -XX:-UseCompressedOops
Java数据类型计算
位是计算机存储数据的最小单位, 每位存储1位的二进制0/1码, 1个字节8位(注意Bits[B] 和 bit的区别 =>字节和位), 字节是计算机处理数据的最小单位
Java中的基本类型, 它们的取值范围是固定的. 且存储于栈空间中, 它们的大小不像多数语言随着机器变化而变化,是程序可移植性保证之一. 对象类型存储于堆空间中
所有的数值类型都有正负号
boolean类型所占空间没有明确指定, 仅定义为能够取字面值true/false
类型 | 大小(字节) | 最小值 | 最大值 | 范围 | (开启压缩)包装类最大空间/bytes |
---|---|---|---|---|---|
byte | 1 | -128 | +127 | -128~127 | 16 |
short | 2 | -215 | +215-1 | - | 16 |
int | 4 | -231 | +231-1 | - | 16 |
long | 8 | -263 | +263-1 | - | 24 |
float | 4 | IEEE754 | IEEE754 | - | 16 |
double | 8 | IEEE754 | IEEE754 | - | 24 |
char | 2 | Unicode o | Unicode 216-1 | - | 16 |
boolean | 1 | - | - | true/false | 16 |
堆空间大小
public static void main(String[] args) {
Byte a = 127;
Integer b = 1000863231;
Short c = 11111;
Long d = 1111111111L;
Float e = 100.001f;
Double f = 200.215d;
Character g = 'a';
Boolean h = true;
//计算本身在堆空间的大小
System.out.println(RamUsageEstimator.sizeOf(a));
System.out.println(RamUsageEstimator.sizeOf(b));
System.out.println(RamUsageEstimator.sizeOf(c));
System.out.println(RamUsageEstimator.sizeOf(d));
System.out.println(RamUsageEstimator.sizeOf(e));
System.out.println(RamUsageEstimator.sizeOf(f));
System.out.println(RamUsageEstimator.sizeOf(g));
System.out.println(RamUsageEstimator.sizeOf(h));
}
再验证类大小
public static void main(String[] args) {
Byte bytes = new Byte((byte) -127);
System.out.println("Obj:" + ClassLayout.parseInstance(bytes).toPrintable());
}
可以看到共占16 bytes 即 16个字节
对象实类大小 = 对象头 + 实例数据 + 内存对齐 =
12B(8B mark word + 4B指向 ) +1B 实例数据 + 3B 对象填充
= 16B
分析集合中情况
上面先暂时这么记, 再分析下集合中String对象的大小情况:
public static void main(String[] args) {
List<String> refList = new ArrayList<>();
System.out.println("oldSize:" + RamUsageEstimator.sizeOf(refList));
System.out.println("oldClass:" + ClassLayout.parseInstance(refList).toPrintable());
for (int i = 0; i<10; i++){
String a = new String();
refList.add(a);
}
System.out.println("newSize:" + RamUsageEstimator.sizeOf(refList));
System.out.println("newClass:" + ClassLayout.parseInstance(refList).toPrintable());
}
确认下集合对象各个循环结束时结果
public static void main(String[] args) {
List<String> refList = new ArrayList<>();
System.out.println("oldSize:" + RamUsageEstimator.sizeOf(refList));
System.out.println("oldClass:" + ClassLayout.parseInstance(refList).toPrintable());
for (int i = 0; i<10; i++){
String a = new String();
refList.add(a);
System.out.println(i + " : newSize:" + RamUsageEstimator.sizeOf(refList));
System.out.println(i + " : newClass:" + ClassLayout.parseInstance(refList).toPrintable());
}
}
oldSize:40
oldClass:java.util.ArrayList object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 5c 02 18 (00000001 01011100 00000010 00011000) (402807809)
4 4 (object header) 12 00 00 00 (00010010 00000000 00000000 00000000) (18)
8 4 (object header) 7e 2f 00 f8 (01111110 00101111 00000000 11111000) (-134205570)
12 4 int AbstractList.modCount 0
16 4 int ArrayList.size 0
20 4 java.lang.Object[] ArrayList.elementData []
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
0 : newSize:120
0 : newClass:java.util.ArrayList object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 5c 02 18 (00000001 01011100 00000010 00011000) (402807809)
4 4 (object header) 12 00 00 00 (00010010 00000000 00000000 00000000) (18)
8 4 (object header) 7e 2f 00 f8 (01111110 00101111 00000000 11111000) (-134205570)
12 4 int AbstractList.modCount 1
16 4 int ArrayList.size 1
20 4 java.lang.Object[] ArrayList.elementData [(object), null, null, null, null, null, null, null, null, null]
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
1 : newSize:144
1 : newClass:java.util.ArrayList object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 5c 02 18 (00000001 01011100 00000010 00011000) (402807809)
4 4 (object header) 12 00 00 00 (00010010 00000000 00000000 00000000) (18)
8 4 (object header) 7e 2f 00 f8 (01111110 00101111 00000000 11111000) (-134205570)
12 4 int AbstractList.modCount 2
16 4 int ArrayList.size 2
20 4 java.lang.Object[] ArrayList.elementData [(object), (object), null, null, null, null, null, null, null, null]
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
2 : newSize:168
2 : newClass:java.util.ArrayList object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 5c 02 18 (00000001 01011100 00000010 00011000) (402807809)
4 4 (object header) 12 00 00 00 (00010010 00000000 00000000 00000000) (18)
8 4 (object header) 7e 2f 00 f8 (01111110 00101111 00000000 11111000) (-134205570)
12 4 int AbstractList.modCount 3
16 4 int ArrayList.size 3
20 4 java.lang.Object[] ArrayList.elementData [(object), (object), (object), null, null, null, null, null, null, null]
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
3 : newSize:192
3 : newClass:java.util.ArrayList object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 5c 02 18 (00000001 01011100 00000010 00011000) (402807809)
4 4 (object header) 12 00 00 00 (00010010 00000000 00000000 00000000) (18)
8 4 (object header) 7e 2f 00 f8 (01111110 00101111 00000000 11111000) (-134205570)
12 4 int AbstractList.modCount 4
16 4 int ArrayList.size 4
20 4 java.lang.Object[] ArrayList.elementData [(object), (object), (object), (object), null, null, null, null, null, null]
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
4 : newSize:216
4 : newClass:java.util.ArrayList object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 5c 02 18 (00000001 01011100 00000010 00011000) (402807809)
4 4 (object header) 12 00 00 00 (00010010 00000000 00000000 00000000) (18)
8 4 (object header) 7e 2f 00 f8 (01111110 00101111 00000000 11111000) (-134205570)
12 4 int AbstractList.modCount 5
16 4 int ArrayList.size 5
20 4 java.lang.Object[] ArrayList.elementData [(object), (object), (object), (object), (object), null, null, null, null, null]
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
5 : newSize:240
5 : newClass:java.util.ArrayList object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 5c 02 18 (00000001 01011100 00000010 00011000) (402807809)
4 4 (object header) 12 00 00 00 (00010010 00000000 00000000 00000000) (18)
8 4 (object header) 7e 2f 00 f8 (01111110 00101111 00000000 11111000) (-134205570)
12 4 int AbstractList.modCount 6
16 4 int ArrayList.size 6
20 4 java.lang.Object[] ArrayList.elementData [(object), (object), (object), (object), (object), (object), null, null, null, null]
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
6 : newSize:264
6 : newClass:java.util.ArrayList object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 5c 02 18 (00000001 01011100 00000010 00011000) (402807809)
4 4 (object header) 12 00 00 00 (00010010 00000000 00000000 00000000) (18)
8 4 (object header) 7e 2f 00 f8 (01111110 00101111 00000000 11111000) (-134205570)
12 4 int AbstractList.modCount 7
16 4 int ArrayList.size 7
20 4 java.lang.Object[] ArrayList.elementData [(object), (object), (object), (object), (object), (object), (object), null, null, null]
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
7 : newSize:288
7 : newClass:java.util.ArrayList object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 5c 02 18 (00000001 01011100 00000010 00011000) (402807809)
4 4 (object header) 12 00 00 00 (00010010 00000000 00000000 00000000) (18)
8 4 (object header) 7e 2f 00 f8 (01111110 00101111 00000000 11111000) (-134205570)
12 4 int AbstractList.modCount 8
16 4 int ArrayList.size 8
20 4 java.lang.Object[] ArrayList.elementData [(object), (object), (object), (object), (object), (object), (object), (object), null, null]
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
8 : newSize:312
8 : newClass:java.util.ArrayList object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 5c 02 18 (00000001 01011100 00000010 00011000) (402807809)
4 4 (object header) 12 00 00 00 (00010010 00000000 00000000 00000000) (18)
8 4 (object header) 7e 2f 00 f8 (01111110 00101111 00000000 11111000) (-134205570)
12 4 int AbstractList.modCount 9
16 4 int ArrayList.size 9
20 4 java.lang.Object[] ArrayList.elementData [(object), (object), (object), (object), (object), (object), (object), (object), (object), null]
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
9 : newSize:336
9 : newClass:java.util.ArrayList object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 5c 02 18 (00000001 01011100 00000010 00011000) (402807809)
4 4 (object header) 12 00 00 00 (00010010 00000000 00000000 00000000) (18)
8 4 (object header) 7e 2f 00 f8 (01111110 00101111 00000000 11111000) (-134205570)
12 4 int AbstractList.modCount 10
16 4 int ArrayList.size 10
20 4 java.lang.Object[] ArrayList.elementData [(object), (object), (object), (object), (object), (object), (object), (object), (object), (object)]
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
Process finished with exit code 0
确认前后的数据对比
前: 集合对象所占堆空间40B, 实例大小 24B
后: 集合空间所占堆空间336B, 实例大小24B
变量条件一致的情况下,影响的就是集合中10个String元素造成的影响
当只添加一个元素的时候大小由40B增加到120B, 而后续的循环中都是24B的步频来增加
初步判断后续的新增元素都是在第一个元素new String(空串) 的基础上类实例上新增的,而常量""都是基于第一个对象的引用
评论区