JUC并发编程:Monitor和对象结构
- 1. Monitor
- 1.1 对象的结构
- 1.1.1 MarkWord
- 1.1.2 Klass Word
- 1.1.3 数组长度
- 1.1.4 🌰
1. Monitor
Monitor官方文档
我们可以把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的反编译文件中并没有看到。这个对象是存放在对象头中的。
1.1 对象的结构
在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)
。所谓对齐填充是指如果对象总大小不是4字节的整数倍,会填充上一段内存地址使之成为整数倍。
下图所示为64位JVM堆中的Java对象结构:
对象头在对象的最前端,包含两部分或者三部分:Mark Word、Klass Word,如果对象是一个数组,那么还可能包含第三部分:数组的长度。
1.1.1 MarkWord
MarkWord主要存储对象运行时的一部分数据,这里面主要包含对象的hashcode、GC年龄分代、synchronized锁信息(锁状态标志位、线程锁标记、偏向线程ID、偏向时间戳)等等。
MarkWord 在32位和64位虚拟机上的大小分别位32bit 和 64bit,它的最后 2 bit 是锁标志位,用来标记当前对象的状态。
1.1.2 Klass Word
里面存的是一个地址,占32位或64位,是一个指向当前对象所属于的类的地址,可以通过这个地址获取到它的元数据信息。klass 包含类的元数据信息,像类的方法,常量池等。你可以把它当作 java 里的 java.lang.Class 对象。
如果应用的对象过多,使用64位的指针将浪费大量内存。64位的JVM比32位的JVM多耗费50%的内存。 现在使用的64位 JVM会默认使用选项+UseCompressedOops 开启指针压缩,将指针压缩至32位。
1.1.3 数组长度
如果这个对象是数组类对象,那么如上图右侧所示,会在对象头里额外的添加一个记录数组长度的字段。
以64位操作系统为例,对象头存储内容图例
- age:Java GC标记位对象年龄。
- identity_hashcode:对象标识Hash码,采用延迟加载技术。当对象使用HashCode()计算后,并会将结果写到该对象头中。当对象被锁定时,该值会移动到线程Monitor中。
- thread:持有偏向锁的线程ID和其他信息。这个线程ID并不是JVM分配的线程ID号,和Java Thread中的ID是两个概念。
- epoch:偏向时间戳。
- ptr_to_lock_record:指向栈中锁记录的指针。
- ptr_to_heavyweight_monitor:指向线程Monitor的指针。
1.1.4 🌰
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());
}
}