java对象内存布局:
-
mark word(记录hashCode值和锁的标识等等)
-
class对象指针
- 类字段
- 补齐位
如果是数组对象,2、3之间应该加上 数组长度
布局排列表:
|
32位jdk 普通对象 |
32位jdk 数组对象 |
64位jdk 未开启指针压缩 普通对象 |
64位jdk 未开启指针压缩 数组对象 |
64位jdk 开启指针压缩 普通对象 |
64位jdk 开启指针压缩 数组对象 |
Mark word(m) |
4 |
4 |
8 |
8 |
8 |
8 |
class指针(c) |
4 |
4 |
8 |
8 |
4 |
4 |
数组长度(l) |
0 |
4 |
0 |
8 |
0 |
4 |
类字段(f) |
|
|
|
|
|
|
补齐位(p) |
|
|
|
|
|
|
单位(byte). 总计 (m+c+l+f+p) % 8 = 0
类字段排列
本文主要简述一下类字段的排列规则。
java对象所占内存大小强制8字节对齐,因此补齐位的存在就是为了补齐8字节,每一个java对象的大小都是8的倍数。(注:本文所说的对象皆为Shallow size,不包含引用对象的大小)
UnSafe
通过 sun.misc.UnSafe#objectFieldOffset(Field) 来获取字段在内存中的偏移量
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
final Unsafe unsafe = (Unsafe) theUnsafe.get(null);
规则一:(m+c+l) %8 =0
类字段排列规则按照先基本类型,后引用类型,大的在前,小的在后,最后按声明顺序排列。对齐以四字节为单位。下为样例代码:
//以64位jdk 未开启指针压缩 普通对象为例
public class Demo {
//32
private String str;
//16
private long l;
//24
private int i;
//30
private byte b;
//28
private short s;
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
final Unsafe unsafe = (Unsafe) theUnsafe.get(null);
System.out.println(unsafe.objectFieldOffset(Demo.class.getDeclaredField("l")));
System.out.println(unsafe.objectFieldOffset(Demo.class.getDeclaredField("i")));
System.out.println(unsafe.objectFieldOffset(Demo.class.getDeclaredField("b")));
System.out.println(unsafe.objectFieldOffset(Demo.class.getDeclaredField("s")));
System.out.println(unsafe.objectFieldOffset(Demo.class.getDeclaredField("str")));
}
}
规则二:(m+c+l) % 8 !=0
优先从基本类型字段中找出一个 字段f,使得 (m+c+l+f) %8 =0,如64位jdk开启指针压缩的普通对象mark word+class refence = 12,如果类字段中有int或者float,会优先把这个字段排在前面 ,如果有多个的话,只会选择排在最前列的那个字段,其余字段按照规则一排列;如果没有int或者float,会根据大小的顺序选择 short char byte boolean,选择一个或多个字段大小和为4byte排列,如果只有一个上述字段,那就只排列一个,然后补空位到4byte。
下为样例代码一:
//以64位jdk 开启指针压缩 普通对象为例
public class Demo2 {
//16
private long l;
//12
private int i;
//24
private String str;
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
final Unsafe unsafe = (Unsafe) theUnsafe.get(null);
System.out.println(unsafe.objectFieldOffset(Demo2.class.getDeclaredField("l")));
System.out.println(unsafe.objectFieldOffset(Demo2.class.getDeclaredField("i")));
System.out.println(unsafe.objectFieldOffset(Demo2.class.getDeclaredField("str")));
}
}
样例代码二:
//以64位jdk 开启指针压缩 普通对象为例
public class Demo2 {
//16
private long l;
//14
private byte b;
//12
private short s;
//24
private String str;
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
final Unsafe unsafe = (Unsafe) theUnsafe.get(null);
System.out.println(unsafe.objectFieldOffset(Demo2.class.getDeclaredField("l")));
System.out.println(unsafe.objectFieldOffset(Demo2.class.getDeclaredField("b")));
System.out.println(unsafe.objectFieldOffset(Demo2.class.getDeclaredField("s")));
System.out.println(unsafe.objectFieldOffset(Demo2.class.getDeclaredField("str")));
}
}
规则三:继承关系
按照规则一和规则二先排列父类字段,再排列子类字段,父类字段加上mark word + class reference 也会八字节对齐