image.png

1.场景:匿名偏向

指第一个线程进入 可偏向但还没有偏向的101锁 的场景

2.场景:重入

重入是指线程t1 还没有释放锁,然后t1又来获取锁。
image.png

重入后的状态

image.pngimage.png

3.场景:重新加锁

重新加锁(再次偏向)是指线程t1 释放偏向锁后,再次访问这个偏向锁。
第二次偏向的性能要比第一次高,因为第一次还需要有一个CAS设置线程id的过程。
image.png

bytecodeInterpreter.cpp

  1. /* monitorenter and monitorexit for locking/unlocking an object */
  2. CASE(_monitorenter): {
  3. //为这个锁 创建一个空的Lock Record
  4. oop lockee = STACK_OBJECT(-1);
  5. CHECK_NULL(lockee);
  6. BasicObjectLock* limit = istate->monitor_base();
  7. BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
  8. BasicObjectLock* entry = NULL;
  9. while (most_recent != limit ) {
  10. if (most_recent->obj() == NULL) entry = most_recent;
  11. else if (most_recent->obj() == lockee) break;
  12. most_recent++;
  13. }
  14. if (entry != NULL) {
  15. //把当前锁对象的markword存储到1ock Record当中的displaced header
  16. entry->set_obj(lockee);
  17. int success = false;
  18. uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;
  19. markOop mark = lockee->mark();
  20. intptr_t hash = (intptr_t) markOopDesc::no_hash;
  21. // 是否开启偏向锁
  22. if (mark->has_bias_pattern()) {
  23. uintptr_t thread_ident;
  24. uintptr_t anticipated_bias_locking_value;
  25. //获取线程id
  26. thread_ident = (uintptr_t)istate->thread();
  27. anticipated_bias_locking_value =
  28. (((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
  29. ~((uintptr_t) markOopDesc::age_mask_in_place);
  30. if (anticipated_bias_locking_value == 0) {
  31. // 值为0 表示已经是偏向自己
  32. if (PrintBiasedLockingStatistics) {
  33. (* BiasedLocking::biased_lock_entry_count_addr())++;
  34. }
  35. success = true;
  36. }
  37. // 偏向禁用
  38. else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
  39. // try revoke bias 禁用:撤销偏向达到40次 从类当中拿到的对象头
  40. markOop header = lockee->klass()->prototype_header();
  41. if (hash != markOopDesc::no_hash) {
  42. header = header->copy_set_hash(hash);
  43. }
  44. if (lockee->cas_set_mark(header, mark) == mark) {
  45. if (PrintBiasedLockingStatistics)
  46. (*BiasedLocking::revoked_lock_entry_count_addr())++;
  47. }
  48. }
  49. //poch_mask_in_place 判断是否过期
  50. else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
  51. // try rebias 重新偏向
  52. // 创建一个偏向当前线程的markword
  53. markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);
  54. if (hash != markOopDesc::no_hash) {
  55. new_header = new_header->copy_set_hash(hash);
  56. }
  57. //将该锁设置为偏向当前线程
  58. if (lockee->cas_set_mark(new_header, mark) == mark) {
  59. if (PrintBiasedLockingStatistics)
  60. (* BiasedLocking::rebiased_lock_entry_count_addr())++;
  61. }
  62. else {
  63. //CAS失败
  64. CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
  65. }
  66. success = true;
  67. }
  68. else {
  69. // 匿名偏向 或者 偏向了别人 这两种情况
  70. // try to bias towards thread in case object is anonymously biased
  71. // 在内存中 创建一个匿名可偏向 header 101
  72. markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place |
  73. (uintptr_t)markOopDesc::age_mask_in_place |
  74. epoch_mask_in_place));
  75. if (hash != markOopDesc::no_hash) {
  76. header = header->copy_set_hash(hash);
  77. }
  78. // 又创建一个偏向自己的new_header thread_id + 101
  79. markOop new_header = (markOop) ((uintptr_t) header | thread_ident);
  80. // debugging hint
  81. DEBUG_ONLY(entry->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);)
  82. // CAS 如果是匿名偏向,就改为偏向自己,将new_header 设置到锁的markword当中去
  83. if (lockee->cas_set_mark(new_header, header) == header) {
  84. if (PrintBiasedLockingStatistics)
  85. (* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
  86. }
  87. else {
  88. //CAS失败
  89. CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
  90. }
  91. success = true;
  92. }
  93. }
  94. // 当前偏向的线程不是自己
  95. // traditional lightweight locking
  96. if (!success) {
  97. //首先产生一个无锁的markword
  98. markOop displaced = lockee->mark()->set_unlocked();
  99. entry->lock()->set_displaced_header(displaced);
  100. bool call_vm = UseHeavyMonitors;
  101. // CAS将这个无锁的markword替换到锁记录中,如果成功则轻量级锁加锁成功
  102. if (call_vm || lockee->cas_set_mark((markOop)entry, displaced) != displaced) {
  103. //进入这个if表示轻量级锁加锁失败
  104. //是否是重入的场景
  105. if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
  106. entry->lock()->set_displaced_header(NULL);
  107. } else {
  108. //重量级锁
  109. CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
  110. }
  111. }
  112. }
  113. //执行CPU的下一条指令
  114. UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
  115. } else {
  116. istate->set_msg(more_monitors);
  117. UPDATE_PC_AND_RETURN(0); // Re-execute
  118. }
  119. }
  120. CASE(_monitorexit): {
  121. oop lockee = STACK_OBJECT(-1);
  122. CHECK_NULL(lockee);
  123. // derefing's lockee ought to provoke implicit null check
  124. // find our monitor slot
  125. BasicObjectLock* limit = istate->monitor_base();
  126. BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
  127. while (most_recent != limit ) {
  128. if ((most_recent)->obj() == lockee) {
  129. BasicLock* lock = most_recent->lock();
  130. markOop header = lock->displaced_header();
  131. most_recent->set_obj(NULL);
  132. if (!lockee->mark()->has_bias_pattern()) {
  133. bool call_vm = UseHeavyMonitors;
  134. // If it isn't recursive we either must swap old header or call the runtime
  135. if (header != NULL || call_vm) {
  136. markOop old_header = markOopDesc::encode(lock);
  137. if (call_vm || lockee->cas_set_mark(header, old_header) != old_header) {
  138. // restore object for the slow case
  139. most_recent->set_obj(lockee);
  140. CALL_VM(InterpreterRuntime::monitorexit(THREAD, most_recent), handle_exception);
  141. }
  142. }
  143. }
  144. UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
  145. }
  146. most_recent++;
  147. }
  148. // Need to throw illegal monitor state exception
  149. CALL_VM(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD), handle_exception);
  150. ShouldNotReachHere();
  151. }

批量重偏向

偏向撤销

等待全局安全点,删除锁记录等等的过程。 撤销很消耗性能。

批量重偏向-轻量级锁降级为偏向锁

当一个线程创建了大量对象并执行了初始的同步操作,后来另一个线程也来将这些对象作为锁对象进行操作,会导偏向锁重偏向的操作。JVM 认为如果对同一个类的对象进行了20次偏向撤销,就会产生批量重偏向。
此时锁 由 轻量级锁 =》偏向锁

批量撤销

批量撤销-禁用偏向锁

JVM 认为如果对同一个类的对象进行了40次单个撤销,JVM会认为代码设计有问题,对所有该类的对象进行批量撤销并且从此对这个类禁用偏向锁,修改类class文件中的markword为001。
在超过40次本应该为可偏向状态偏向锁101的新对象,在经历过批量重偏向和批量撤销后直接在实例化后转为无锁001
image.png

  • 批量重偏向和批量撤销是针对类的优化,和对象无关。
  • 偏向锁重偏向一次之后不可再次重偏向。
  • 当某个类已经触发批量撤销机制后,JVM会默认当前类产生了严重的问题,剥夺了该类的新实例对象使用偏向锁的权利

偏向过期

image.png
当一个类被加载到元空间后,这个类的类信息会成为创建这个类的对象的模板,这个epoch的值就是存在于类信息中的模板值,默认为01。
当发生批量重偏向时,就会对元空间中这个类的epoch值 +1。
当一个线程访问偏向锁,发现epoch值不等于初始值时,就认为这个偏向锁过期了,可以直接把该锁设置为偏向自己。