梦想还是要有的,万一忘了咋办?

0%

Java引用数据类型

介绍

Java里除了基础数据类型就是引用数据类型。例如:类、接口、数组、枚举、标注

对象内存布局

内存布局

对象头

HotSpot虚拟机的对象头包括两部分信息:

markword

用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为4Byte和8Byte

klass

对象头的另外一部分是klass类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例.在32位虚拟机中 占用 4Byte,64位虚拟机 占 8Byte ,开启压缩后占 4Byte。

数组长度(只有数组对象有)

如果对象是一个数组, 那在对象头中还必须有一块数据用于记录数组长度,32位虚拟机 4Byte,64位虚拟机 8Byte,64位(压缩) 4Byte(这4Byte 来自 数据指针压缩出来的)

实际数据

实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都需要记录起来。

对齐填充

不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或者2倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

对象头大小

系统 MarkWord 数据指针 数组长度 非数组对象 数组对象
32位 4 4 4 8 12
64位(指针未压缩) 8 8 8 16 24
64位(指针压缩) 8 4 4 12 16
1
* 静态属性不算在对象大小内。

算一算

String对象大小

String str=“abc”;
计算下 str 对象的大小??(64位、未压缩)

String 非静态变量
String

  • char value[] 对象大小:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    public static void main(String[] args) {
System.out.println(VM.current().details());
char[] str=new char[]{'a','b','c'};
ClassLayout layout = ClassLayout.parseInstance(str);
System.out.println(layout.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]

[C 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) 08 f2 ad 14 (00001000 11110010 10101101 00010100) (346944008)
12 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
16 4 (object header) 03 00 00 00 (00000011 00000000 00000000 00000000) (3)
20 4 (alignment/padding gap)
24 6 char [C.<elements> N/A
30 2 (loss due to the next object alignment)
Instance size: 32 bytes
 Space losses: 4 bytes internal + 2 bytes external = 6 bytes total
结论:
24B(对象头)+3*2B(数据)+2B(对齐)=32B
  • int hash 对象大小:
    4B
  • String 对象本身大小:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
String str="abc";
ClassLayout layout = ClassLayout.parseInstance(str);
System.out.println(layout.toPrintable());

=========输出==============

# 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]

java.lang.String 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) c0 2f 22 03 (11000000 00101111 00100010 00000011) (52572096)
12 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
16 8 char[] String.value [a, b, c]
24 4 int String.hash 0
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
结论:
16(对象头)+8(char[]引用)+32(char[])+4(hash)+4(补齐)=64B

64位虚拟机压缩、jdk1.8:
12(对象头)+4(char[]引用)+24(char[])+4(hash)+4(补齐)=48B

方法&静态属性

答案:不影响

方法数量越多对象内存会变大吗?
静态属性影响对象内存吗?

测试代码

1
2
3
4
5
6
7
public static class A {
}

A a=new A();
ClassLayout layout = ClassLayout.parseInstance(a);
System.out.println(layout.toPrintable());

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# WARNING | Compressed references base/shifts are guessed by the experiment!
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

com.hardydou.jmm.ObjectSize$A 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) 16 f0 00 f8 (00010110 11110000 00000000 11111000) (-134156266)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

增加放方法、静态属性后

1
2
3
4
5
6
7
8
 public static class A {
static int a;
private void m1(){}
private void m2(){}
}
A a=new A();
ClassLayout layout = ClassLayout.parseInstance(a);
System.out.println(layout.toPrintable());

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

com.hardydou.jmm.ObjectSize$A 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) 16 f0 00 f8 (00010110 11110000 00000000 11111000) (-134156266)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total