java对象头中都存了些什么?

32位jdk中:

锁状态 25bit 4bit 1bit 2bit
23bit 2bit   偏向锁标志位(biased_lock) 锁状态(lock)
正常对象(normal object) 对象hashcode(hash) 对象分代年龄(age) 0 01
偏向锁(biased object) 线程ID Epoch 对象分代年龄(age) 1 01
轻量级锁 指向栈中锁记录指针 00
重量级锁 指向互斥量(重量级锁)的指针 10
GC标记 11

64位jdk中:

锁状态 25位 31位 1位 4位 1位 2位
29位 2位     偏向锁标志位(biased_lock) 锁状态(lock
正常对象(normal object) 未使用 对象hashcode(hash) 未使用 对象分代年龄(age) 0 01
偏向锁(biased object) 线程ID Epoch 未使用 对象分代年龄(age) 1 01

在javaSE1.6中,为了减少上下文切换带来的性能消耗,jdk引入了偏向锁与轻量锁

synchronized锁保证线程安全时锁的升级过程:

偏向锁:由于重量级锁在每次释放与获取锁时进行上下文切换对性能消耗大,而多数锁的获取与释放常常在同一个线程中进行,针对该现象引入了偏向锁进行优化;当一个线程访问同步代码块并获取锁时,会在对象头和栈帧中的所记录里存储偏向锁的线程ID,以后该线程在进入和退出同步代码块时,不用进行CAS操作来加锁解锁,只需要测试对象头的Mark World中是否存储着指向当前线程的偏向锁,如果测试成功,表示该线程已经获取到锁进行执行,如果测试失败,则需要再次测试Mark world中偏向锁标识位是否为1(表示位偏向锁),如果没有设置,则开始使用轻量级锁(CAS)进行竞争锁,如果是,则尝试使用CAS将对象头的偏向锁指向当前线程

偏向锁撤销:当有另一个线程进行竞争(竞争出现)这把锁时,才会撤销偏向锁,撤销的过程要等到全局安全点(在这个时间点上没有代码正在执行)才会进行,撤销前要西安暂停拥有偏向锁的线程,再检查该线程是否存活,如果不存活,则将对象头设位无所,否则拥有偏向锁的栈会被执行,遍历偏向对象的锁记录,栈中所记录和对象头的mark world要么偏向于其他线程,要么恢复到无锁或者标记对象不适合作为偏向锁,最后唤醒暂停的线程

偏向锁会在程序启动几秒后激活:可以通过-XX:BiasedLockingStartupDelay=0来消除这个延时

如果线程中的锁通常处于竞争状态,可以通过-XX:-UseBiasedKocking=false来关闭偏向锁

java对象头与synchronized锁的升级过程_上下文切换

轻量级锁:

  加锁:线程执行同步代码块前,jvm再当前线程的栈帧中创建用于存储所记录的空间,并将对象头中的mark word复制到所记录中,官方称为displaced mark word。然后线程尝试使用CAS将对象头的mark word替换为指向锁记录的指针。如果成功当前线程获取锁,否则其他线程获得锁,当前线程CAS等待

  解锁:轻量级锁解锁时,会使用原子的CAS操作将Displaced mark word替换回对象头,如果成功,则表示没有竞争,如果失败,则表示该锁当前存在竞争,锁会升级为重量级锁

 java对象头与synchronized锁的升级过程_加锁_02

 

 重量级锁:为了避免无用的自旋,一当锁升级为重量级锁,就不会恢复到轻量级锁,当锁为重量级锁的状态下,其他线程试图获取锁时,都将处于阻塞状态,当持有锁的线程释放锁时,会唤醒其他线程,进行新一轮锁的竞争