上一篇我们从HotSpot的源码入手,介绍了Java的对象模型。这篇文章是在上一篇文章的基础上介绍Java对象头的。主要介绍对象头的作用和结构及其与锁的关系。Java对象模型回顾与勘误上一篇文章中对象头的描述有误,我已经在我的博文中更正了。这里重申一下。对于每一个Java类,当它被JVM加载时,JVM会为这个类创建一个instanceKlass,保存在方法区,在JVM层用它来表示Java类。当我们在Java代码中使用new创建对象时,JVM会创建一个instanceOopDesc对象,其中包含对象头和实例数据。这里说的objectheader到底是什么?classoopDesc{friendclassVMStructs;private:volatilemarkOop_mark;union_metadata{wideKlassOop_klass;narrowOop_compressed_klass;}_metadata;}上面代码中的_mark和_metadata其实就是对象头的定义。关于_metadata之前已经介绍过,这里不再赘述。由于本题主要是想介绍JAVA并发相关的知识,所以本文介绍_mark,也就是标记词。对象头信息是额外的存储成本,与对象本身定义的数据无关。考虑到虚拟机的空间效率,MarkWord被设计成一种非固定的数据结构,在很小的空间内存储尽可能多的信息。它会根据对象的状态重用自己的存储空间。标记的设计方式与网络协议头很相似:标记字被分成多个比特区间,在不同的对象状态下比特??被赋予不同的含义。下图描述了对象在32位虚拟机上不同状态下markword的各个位范围的含义。同样,在HotSpot的源码中,我们可以找到objectheader对象的定义,这将一一印证上图中的描述。对应于markOop.hpp类。enum{age_bits=4,lock_bits=2,biased_lock_bits=1,max_hash_bits=BitsPerWord-age_bits-lock_bits-biased_lock_bits,hash_bits=max_hash_bits>31?31:max_hash_bits,cms_bits=LP64_ONLY(1)NOT_LP64(0),epoch_bits=2};从上面的枚举定义可以看出,对象头主要包含GC分代年龄、锁状态标记、hash码、epoch等信息。从上图可以看出,一个对象有五种状态,分别是无锁态、轻量级锁、重量级锁、GC标记和偏向锁。在32位虚拟机中,有两个Bit是用来存放lockmarks的,但是我们都知道两个bit最多只能表示四种状态:00,01,10,11,那么第五种状态怎么表示它,需要额外依赖1Bit的空间,用0和1来区分。32位HotSpot虚拟机在对象未被锁定的状态下,MarkWord的32Bits空间中的25Bits用于存储对象哈希码(HashCode),4Bits用于存储对象的世代年龄,而2Bits用于存放锁标志位,1Bit固定为0,表示非偏向锁。markOop.hpp类中有对象状态的定义:enum{locked_value=0,unlocked_value=1,monitor_value=2,marked_value=3,biased_lock_pattern=5};简单翻译一下:locked_value(00)=0unlocked_value(01)=1monitor_value(10)=2marked_value(11)=3biasedlockpattern(101)=5关于为什么要定义这么多状态,轻量级锁、重量级锁、偏向锁以及它们之前的关系上面提到的会在下一篇文章中重点介绍。【本文为专栏作家霍利斯原创文章,作者微信公众号Hollis(ID:hollishuang)】点此阅读更多本作者好文
