关于对象头以及Synchronized锁状态的理解

最近在看OpenJdk的源码,对对象头部分有了一个更直观的理解。同时,对象头中的MarkWord部分,又包含了对不同锁状态的标识,故以此为起点,梳理下不同锁状态的表示及不同锁状态的转换。

1、对象头

    JVM内部,使用 OOP-Klass 二分模型来表示类和对象,OOP用来表示Java对象实例,Klass用来表示Java类。在Java应用程序运行过程中,每创建一个Java对象,在JVM内部就会相应地创建一个OOP对象来对应(具体为instanceOopDesc对象)。而每加载一个Java类,也会对应创建一个Klass对象来对应(具体为instanceKlass对象)。

    Java对象在内存中的布局,可分为连续的两部分:instanceOopDesc + 实例数据。数组对象为:arrayOopDesc + 实例数据。

关于对象头以及Synchronized锁状态的理解_第1张图片

    这里的 instanceOopDesc 与 arrayOopDesc,即为对象头。对象头中的Mark Word,即 OopDesc 基类中的 _mark 成员,存储对象运行时信息,如Hash Code、Age、锁状态标识、线程持有的锁、偏向线程ID、偏向时间戳等。对象头中的元数据指针,即 OopDesc 基类中的 _metadata 成员,指向描述对象所对应 Java 类的 Klass 对象。

2、锁状态

     目前 Synchronized 锁状态有四种,级别从低到高分别是:无锁、偏向锁、轻量级锁和重量级锁。对应到 Mark Word 里,锁状态标识如下图所示:

对象状态 25bit 4bit 1bit 2bit
23bit 2bit 是否偏向锁 锁标识
Unlocked Hashcode Age 0 01
Light-weight locked 锁记录指针 00
Heavy-weight locked Monitor指针 10
Mraked for GC Forward指针 11
Biased/biasable 线程ID Epoch Age 1 01

    无锁:无锁不锁定资源,所有线程都能访问并修改同一资源,但同时只有一个线程能修改成功。

    偏向锁:Java6 中加入,如果一段同步代码,只被同一个线程访问,那么该线程会自动获取锁。

    轻量级锁:Java6 中加入,偏向锁被其他线程访问时,就会升级为轻量级锁,其他线程通过自旋尝试获取锁,不阻塞。

    重量级锁:等待锁的线程都会进入阻塞状态。

3、加锁解锁及锁状态转换

    1、偏向锁

    当一个线程访问同步块并获取锁时,会在对象头的 Mark Word 和栈帧中的锁记录里存储锁偏向的线程ID。以后该线程在进入和退出该同步块时,直接检测当前 Mark Word 中存储的线程ID是否指向当前线程,如果是,则表示已获取锁。否则,如果 Mark Word 中偏向锁标识已被设置为1,则尝试使用CAS将 Mark Word 中线程ID指向当前线程。

    偏向锁不会主动释放,当有其他线程尝试获取锁时,才会释放锁。偏向锁的撤销,需等到全局安全点。首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活动,如不处于活动状态,则将 Mark Word 设置为无锁状态;否则,拥有偏向锁的栈被执行,栈中的锁记录和 Mark Word 中的锁状态恢复到无锁或者升级为轻量级锁,最后唤醒暂停的线程。

    2、轻量级锁

    

你可能感兴趣的:(Java)