java对象头分析
一:对象头介绍hotSpot虚拟机中,对象在内存中的存储布局可以分为三块区域:对象头(header),实例数据(Insrance Data)和对齐填充(Padding).HotSpot虚拟机的对象头(Object Header)包括两部分信息,第一部分用于存储对象自身的运行时数据,如:哈希码(HashCode),GC分带年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等等,这部分数据的..
一:对象头介绍
hotSpot虚拟机中,对象在内存中的存储布局可以分为三块区域:对象头(header),实例数据(Insrance Data)和对齐填充(Padding).
HotSpot虚拟机的对象头(Object Header)包括两部分信息,第一部分用于存储对象自身的运行时数据,如:哈希码(HashCode),GC分带年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂不考虑开启指针压缩的场景)中分别为32个和64个bits,官方称它为"Mark Word"。
注意:以下的都是基于64位操作系统来分析的。
下面是截取一张hotSpot源码当中的注释(markword的结构,定义在markOop.hpp文件)
java对象头在不同状态下有不同的表现形式,主要有三种状态:无锁状态,加锁状态,gc标记状态。
下面这两幅图可以更清晰的说明java中对象头上的Mark Word的组成以及状态。
二 证明对象头
(1)证明基本数据类型所占的byte(B)
public class ZPF {
}
public class JOLExample1 {
static ZPF a = new ZPF();
public static void main(String[] args) {
//jvm的信息
out.println(VM.current().details());
out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
打印出的结果如下:
# Running 64-bit HotSpot VM.
# Using compressed oop with 0-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.luban.layout.ZPF 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) 43 c1 00 20 (01000011 11000001 00000000 00100000) (536920387)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
这里Field sizes by type: 4(跳过这个),
1, 1, 2, 2, 4, 4, 8, 8 [bytes]分别对应
boolean,byte,char,short,int,float,long,double的大小(从最后开始对应)
可以看到整个对象一共16B,其中对象头(Object header)12B,还有4B是对齐字节。
那么问题来了,什么叫做对象的实例数据?对象头里面的12B存的到底是什么呢?
(2)证明boolean确实占一个byte
可以看到我只在ZPF这个实体类里面添加了一个boolean的类型
打印结果如下:
其他的基本数据类型,你可以自己测一下。
我们可以看到整个对象是16byte,其中12个是对象头,boolean占1个byte,剩下3个byte是对齐填充。那么12个B存储的是什么呢?
(3)对象头中的存储结构
oracle官网是这样描述的:
object header
Common structure at the beginning of every GC-managed heap object. (Every oop points to an object header.) Includes fundamental information about the heap object’s layout, type, GC state, synchronization state, and identity hash code. Consists of two words. In arrays it is immediately followed by a length field. Note that both Java objects and VM-internal objects have a common object header format.
每个gc管理的堆对象开头的公共结构。(每个oop都指向一个对象头。)包括堆对象的布局、类型、GC状态、同步状态和标识哈希码的基本信息。由两个词组成。在数组中,它后面紧跟着一个长度字段。注意,Java对象和vm内部对象都有一个通用的对象头格式。
这两个词是什么呢?
mark word
The first word of every object header. Usually a set of bitfields including synchronization state and identity hash code. May also be a pointer (with characteristic low bit encoding) to synchronization related information. During GC, may contain GC state bits.
每个对象头的第一个单词。通常是一组位域,包括同步状态和标识哈希码。也可以是指向同步相关信息的指针(具有特征的低比特编码)。在GC期间,可能包含GC状态位。
klass pointer
The second word of every object header. Points to another object (a metaobject) which describes the layout and behavior of the original object. For Java objects, the “klass” contains a C++ style “vtable”.
每个对象头的第二个单词。指向描述原始对象的布局和行为的另一个对象(元对象)。对于Java对象,“klass”包含一个c++风格的“vtable”。
其实就是:
这里推理一下:其中由上图可知markword占8byte(64bit),所以klassword占4byte。
那么接下来我们就来分析,不同锁的状态下markword中的变化。
(4)无锁状态下的对象头信息
public class ZPF {
boolean flag = false;
}
public static void main(String[] args) throws Exception {
ZPF a = new ZPF();
out.println("befor hash");
//没有计算HASHCODE之前的对象头
out.println(ClassLayout.parseInstance(a).toPrintable());
//JVM 计算的hashcode
out.println("jvm------------0x"+Integer.toHexString(a.hashCode()));
//当计算完hashcode之后,我们可以查看对象头的信息变化
out.println("after hash");
out.println(ClassLayout.parseInstance(a).toPrintable());
}
打印结果如下:
在来看一下:无锁状态下,前64位的存储。
注意:这里对应的值是从后往前对应的,因为是小端存储。
(5)偏向锁对象头分析
static ZPF a;
public static void main(String[] args) throws Exception {
Thread.sleep(5000);//虚拟机启动时对偏向锁有延时
a= new ZPF();
synchronized (a){
out.println("lock ing");
out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
打印结果如下:
偏向锁状态下前64位:
(6)轻量级锁对象头分析
static ZPF a;
public static void main(String[] args) throws Exception {
a = new ZPF();
out.println("befre lock");
out.println(ClassLayout.parseInstance(a).toPrintable());
sync();
out.println("after lock");
out.println(ClassLayout.parseInstance(a).toPrintable());
}
public static void sync() throws InterruptedException {
synchronized (a){
out.println("lock ing");
out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
打印结果如下:
轻量级锁前64位结构图:
(7)重量级锁对象头分析
static ZPF a;
public static void main(String[] args) throws Exception {
a = new ZPF();
out.println("befre lock");
out.println(ClassLayout.parseInstance(a).toPrintable());//无锁
Thread t1= new Thread(){
public void run() {
synchronized (a){
try {
Thread.sleep(5000);
System.out.println("t1 release");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.start();
Thread.sleep(1000);
out.println("t1 lock ing");
out.println(ClassLayout.parseInstance(a).toPrintable());//轻量锁
sync();
System.gc();
out.println("after gc()");
out.println(ClassLayout.parseInstance(a).toPrintable());//无锁---gc
}
public static void sync() throws InterruptedException {
synchronized (a){
System.out.println("t1 main lock");
out.println(ClassLayout.parseInstance(a).toPrintable());//重量锁
}
}
打印结果如下:
重量级锁前64位结构如下:
更多推荐
所有评论(0)