概述
Java对象的内存布局如下,以下hotspot源码都是基于openjdk 1.8
1个字宽大小取决于操作系统,32位操作系统1个字宽为4个字节,64位则为8个字节
- 1个字宽:Mark Word
- 1个字宽:Class pointer
- 1个字宽:数组长度(如果是数组对象的话)
N个字宽,取决于成员变量
openjdk 1.8 对象头源码如下
class oopDesc {
friend class VMStructs;
private:
volatile markOop _mark;
union _metadata {
Klass* _klass;
narrowKlass _compressed_klass;
} _metadata;
}
对象头
Mark Word
在hotspot/src/share/vm/oops/markOop.hpp
其中lock的两个标志位如下
指针压缩
在32位里面采用的指针大小为4个字节,但是在64位的操作系统下面如果堆内存不超过32G,就开启指针压缩功能,可以使用32位的指针表示32G的地址(原本32位的指针只能表示4G的地址)
原因
因为64位的vm都是以8个字节对齐的(1字宽),意味着在java中所有的对象地址都是8的倍数,即低3位都是000,所以可以重用这三位,做一个映射:真实地址 = 压缩地址<<3,所以32位原本只能表示4G的地址就可以增加到32G
-XX:+/-UseCompressedOops参数可以控制是否开启指针压缩
实例域
控制实例域布局的参数-XX:FieldsAllocationStyle=mode,mode可以是0,1,2
在Java中数据类型中分为两种
- long / double - 8 bytes
- int / float - 4 bytes
- short / char - 2 bytes
- byte/boolean - 1 bytes
- reference type - 4 or 8 bytes
为了方便观看可以使用openjdk提供的jol工具,对一下Son类进行打印
public static class Son extends Father {
Object filed1;
char field2;
short field3;
Object filed4;
long field5;
byte field6;
double filed7;
}
public static class Father{
Object filed1;
char field2;
short field3;
byte field4;
Object filed5;
}
在布局的过程中,对实例域的布局遵循尽可能适合的方式以减少间隙,因为由于填充会形成gap空洞, 比如使用压缩指针时, 头占12字节, 后面如果是long的话, long的对齐要求是8字节, 中间会有4个字节的空洞, 为了高效利用, 可以把int/short/byte等比较小的对象塞进去, 与此同时JVM提供了开关控制该特性-XX:+/-CompactFields, 默认开启.
FieldsAllocationStyle=0
这种布局模式是先排父类再到子类,并且引用类型在前面,基本数据类型在后面
使用jol打印的结果如下
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) 82 c1 00 f8 (10000010 11000001 00000000 11111000) (-134168190)
12 4 java.lang.Object Father.filed1 null
16 4 java.lang.Object Father.filed5 null
20 2 char Father.field2
22 2 short Father.field3 0
24 1 byte Father.field4 0
25 3 (alignment/padding gap)
28 4 java.lang.Object Son.filed1 null
32 4 java.lang.Object Son.filed4 null
36 2 char Son.field2
38 2 short Son.field3 0
40 8 long Son.field5 0
48 8 double Son.filed7 0.0
56 1 byte Son.field6 0
57 7 (loss due to the next object alignment)
Instance size: 64 bytes
Space losses: 3 bytes internal + 7 bytes external = 10 bytes total
FieldsAllocationStyle=1(默认)
这种布局模式是先排父类再到子类,并且基本数据型在前面,引用类型在后面
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) 82 c1 00 f8 (10000010 11000001 00000000 11111000) (-134168190)
12 2 char Father.field2
14 2 short Father.field3 0
16 1 byte Father.field4 0
17 3 (alignment/padding gap)
20 4 java.lang.Object Father.filed1 null
24 4 java.lang.Object Father.filed5 null
28 2 char Son.field2
30 2 short Son.field3 0
32 8 long Son.field5 0
40 8 double Son.filed7 0.0
48 1 byte Son.field6 0
49 3 (alignment/padding gap)
52 4 java.lang.Object Son.filed1 null
56 4 java.lang.Object Son.filed4 null
60 4 (loss due to the next object alignment)
Instance size: 64 bytes
Space losses: 6 bytes internal + 4 bytes external = 10 bytes total
FieldsAllocationStyle=2
这种布局模式是先排父类与子类的引用类型相靠近
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) 82 c1 00 f8 (10000010 11000001 00000000 11111000) (-134168190)
12 2 char Father.field2
14 2 short Father.field3 0
16 1 byte Father.field4 0
17 3 (alignment/padding gap)
20 4 java.lang.Object Father.filed1 null
24 4 java.lang.Object Father.filed5 null
28 4 java.lang.Object Son.filed1 null
32 4 java.lang.Object Son.filed4 null
36 2 char Son.field2
38 2 short Son.field3 0
40 8 long Son.field5 0
48 8 double Son.filed7 0.0
56 1 byte Son.field6 0
57 7 (loss due to the next object alignment)
Instance size: 64 bytes
Space losses: 3 bytes internal + 7 bytes external = 10 bytes total
中间的20~32就是所有的引用类型
OopMapBlock
这个数据结构代码如下hotspot/src/share/vm/oops/instanceKlass.hpp
class OopMapBlock VALUE_OBJ_CLASS_SPEC {
private:
int _offset;
uint _count;
};
这个互数据结构是用来记录这个类的域的偏移量和数量,目的是为了GC扫描存活对象的时候访问该类的引用对象,完成可达性分析
在上面3中域的布局中,0,1都需要2个OopMapBlock,而2只需要一个,因为对象里面的引用类型都是连续的
|