我们可以把Monitor理解为一个同步工具,也可以认为是一种同步机制。它通常被描述为一个对象,所有的Java对象都是天生的Monitor,每一个Java对象都有成为Monitor的潜质。因为在Java的设计中 ,每一个Java对象都带了一把看不见的锁,它叫做内部锁或者Monitor锁。
Monitor 是线程私有的数据结构,每一个线程都有一个可用monitor record列表,同时还有一个全局的可用列表。每一个被锁住的对象都会和一个monitor关联(对象头的MarkWord中的LockWord指向monitor的起始地址),同时monitor中有一个Owner字段存放拥有该锁的线程的唯一标识,表示该锁被这个线程占用。
moniter结构:
获取对象锁的过程,其实是获取monitor对象的所有权的过程。哪个线程持有了monitor对象,那么哪个线程就获得了锁,获得了锁的对象可以重复的来获取monitor对象,但是同一个线程每获取一次monitor对象所有权锁计数就加一,在解锁的时候也是需要将锁计数减成0才算真的释放了锁。
monitor对象,我们其实在Java的反编译文件中并没有看到。这个对象是存放在对象头中的。
在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)
。所谓对齐填充是指如果对象总大小不是4字节的整数倍,会填充上一段内存地址使之成为整数倍。
下图所示为64位JVM堆中的Java对象结构:
对象头在对象的最前端,包含两部分或者三部分:Mark Word、Klass Word,如果对象是一个数组,那么还可能包含第三部分:数组的长度。
MarkWord主要存储对象运行时的一部分数据,这里面主要包含对象的hashcode、GC年龄分代、synchronized锁信息(锁状态标志位、线程锁标记、偏向线程ID、偏向时间戳)等等。
MarkWord 在32位和64位虚拟机上的大小分别位32bit 和 64bit,它的最后 2 bit 是锁标志位,用来标记当前对象的状态。
里面存的是一个地址,占32位或64位,是一个指向当前对象所属于的类的地址,可以通过这个地址获取到它的元数据信息。klass 包含类的元数据信息,像类的方法,常量池等。你可以把它当作 java 里的 java.lang.Class 对象。
如果应用的对象过多,使用64位的指针将浪费大量内存。64位的JVM比32位的JVM多耗费50%的内存。 现在使用的64位 JVM会默认使用选项+UseCompressedOops 开启指针压缩,将指针压缩至32位。
如果这个对象是数组类对象,那么如上图右侧所示,会在对象头里额外的添加一个记录数组长度的字段。
以64位操作系统为例,对象头存储内容图例
class Student{
boolean flag=false;
}
public class Test1 {
public static void main(String[] args) {
Student student=new Student();
System.out.println(ClassLayout.parseInstance(student).toPrintable());
}
}