专栏首页立权的博客Java锁膨胀过程中的解惑:1.单线程不一定能获取偏向锁 ; 2.安全点重偏向不能直接占有仍有效偏向锁

Java锁膨胀过程中的解惑:1.单线程不一定能获取偏向锁 ; 2.安全点重偏向不能直接占有仍有效偏向锁

1.偏向锁的获取逻辑主要在汇编实现的 biased_locking_enter 方法中

请求线程进入这个方法之后,会直接判断锁依赖对象的对象头是否有 偏向锁 成分(101是偏向锁成分)。

如果没有,直接跳出,退出后直接尝试把自己栈上的BiasLock 地址写入对象头,写入成功的前提是锁依赖对象没加锁(001是没加锁)

这样的话,岂不是所有未加锁对象,进入biased_locking_enter都会退出,直接设置轻量级锁成功?

那么开启轻量级锁有什么用呢?

问题在于,如果锁依赖对象的 Klass 的 prototype_header 中的 后几位是 101 ,也就是偏向锁成分。

那么这个 Klass 的对象在创建的时候,都会直接把后几位设置成 101,但是没有写入 JavaThread* ,也就是匿名偏向锁(anonymous bias_lock)

所以会在 biased_locking_enter 获得偏向锁成功。

2.在 revoke_and_rebais 的最后,当 撤销偏向锁到达了一定频率,则会触发 请求线程使用 VM_Thread::execute 提交 VM_BulkRevokeBias 这个 operation 给VM_Thread 执行

需要注意的是 VM_BulkRevokeBias 这个 Operation 是直接继承自 VM_Operation 的,调用evaluate_concurrently时

默认使用VM_Operation的虚函数,默认返回 false,也就是 VM_Thread::execute 提交 Operation 请求以后会阻塞在提交点。

VM_BulkRevokeBias 会在安全点(所有Operation都是在安全点执行的)调用bulk_revoke_or_rebias_at_safepoint

这个函数 和 revoke_and_rebias 是膨胀过程中 唯二能返回 BIAS_REVOKED_AND_REBIASED 状态的函数

返回这个状态表示锁依赖对象的对象头中的 JavaThread* 被置为当前线程的 JavaThread* 了。也就是当前线程获取偏向锁成功。也就是重偏向成功。

但是有一个疑问,在函数的末尾貌似没有判断 对象头的 epoch 是否有效,直接就把自己的JavaThread*写进去了,我很是不解。

虽然末尾要在 bulk_rebias 为 true ,attempt_rebias_of_object 都为 true 才会执行。

但是 bulk_rebias 在 update_heuristics 调用较为频繁(revoke_and_rebias较为频繁,也就是偏向锁撤销较为频繁)的时候会 就会为true

attempt_rebias_of_object 在 faster_enter 处被决定,通过 synchronized 进入 faster_enter 时,会是 true

所以这两个值不难成立,为什么就能直接获得偏向锁呢?现在的线程可还在synchronized块里。

但是实际上,我忽略了 bulk_rebias 为 true,会进入的分支里 ,会调用 revoke_bias。

revoke_bias 在偏向锁被其他线程占有的时候,会直接把偏向锁膨胀成轻量级锁。

这样的话,对象头的 偏向成分就会消失(101是偏向成分,bias_pattern),这样的话,末尾的 o->mark()->has_bias_pattern()

就不会成立。

static BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint(oop o,
                                                                   bool bulk_rebias,
                                                                   bool attempt_rebias_of_object,
                                                                   JavaThread* requesting_thread) {

  jlong cur_time = os::javaTimeMillis();
  o->blueprint()->set_last_biased_lock_bulk_revocation_time(cur_time);

  klassOop k_o = o->klass();
  Klass* klass = Klass::cast(k_o);

  if (bulk_rebias) {
    if (klass->prototype_header()->has_bias_pattern()) {
      int prev_epoch = klass->prototype_header()->bias_epoch();
      klass->set_prototype_header(klass->prototype_header()->incr_bias_epoch());
      int cur_epoch = klass->prototype_header()->bias_epoch();


      for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
        GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
        for (int i = 0; i < cached_monitor_info->length(); i++) {
          MonitorInfo* mon_info = cached_monitor_info->at(i);
          oop owner = mon_info->owner();
          markOop mark = owner->mark();
          if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
            assert(mark->bias_epoch() == prev_epoch || mark->bias_epoch() == cur_epoch, "error in bias epoch adjustment");
            owner->set_mark(mark->set_bias_epoch(cur_epoch));
          }
        }
      }
    }
  // 被忽略的分支
    revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread);
  } else {
   
    klass->set_prototype_header(markOopDesc::prototype());

    for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
      GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
      for (int i = 0; i < cached_monitor_info->length(); i++) {
        MonitorInfo* mon_info = cached_monitor_info->at(i);
        oop owner = mon_info->owner();
        markOop mark = owner->mark();
        if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
          revoke_bias(owner, false, true, requesting_thread);
        }
      }
    }

    revoke_bias(o, false, true, requesting_thread);
  }
  BiasedLocking::Condition status_code = BiasedLocking::BIAS_REVOKED;
 //末尾 bulk_rebias 为 true ,attempt_rebias_of_object 都为 true 才会执行
  if (attempt_rebias_of_object &&
      o->mark()->has_bias_pattern() &&
      klass->prototype_header()->has_bias_pattern()) {
    markOop new_mark = markOopDesc::encode(requesting_thread, o->mark()->age(),
                                           klass->prototype_header()->bias_epoch());
    o->set_mark(new_mark);
    status_code = BiasedLocking::BIAS_REVOKED_AND_REBIASED;
  }return status_code;
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • elasticsearch-基本概念

    先类比 elasticsearch 和 关系型数据库对应的某些概念,了解ES的结构:

    执生
  • elasticsearch-分片思想

    最好的情况是,我们能知道自己对单个分片的要求,比如单个分片对用户的响应时间不能长于哪个值,测试的方法是使用历史数据,压入到一个测试的分片中

    执生
  • Redis - sentinel / cluster

    订阅连接:订阅某个频道,频道有消息马上读取,一个频道上的消息会发给多个订阅者,所以是一发多收

    执生
  • LollipopGo实战小组--leaf并发压力测试文档

    先来火焰图,通过火焰图分析,发现encoding/json 解析数据的时候占用了大量时间,可以考虑更换github.com/json-iterator/go

    李海彬
  • Redis 哨兵 Sentinel

    redis-server /path/to/sentinel.conf --sentinel

    WindWant
  • 弹性服务器架构-连接一切的力量

    一颗芯片,寄存器通过总线向运算器输送数据。一台服务器,内存通过DDR总线与处理器完成数据互动。一个数据中心,存储集群通过以太网与计算集群形成对数据流的处理和加工...

    鹅厂网事
  • 那些拼命加班的程序员们,后来都怎么样了?

    小张属于踏实肯干的程序员,在公司工作兢兢业业,也干出了很不错的成绩,当然,与之伴随的是,加班成为了家常便饭。

    黄小斜
  • 从WebShell到域控实战详解

    首先介绍此次渗透的环境:假设我们现在已经渗透了一台服务器PAVMSEF21,该服务器内网IP为10.51.0.21。扫描后发现内网网络结构大概如图所示,其中PA...

    Ms08067安全实验室
  • 从WebShell到域控实战详解

    首先介绍此次渗透的环境:假设我们现在已经渗透了一台服务器PAVMSEF21,该服务器内网IP为10.51.0.21。扫描后发现内网网络结构大概如图所示,其中PA...

    用户1631416
  • 打卡天津,腾讯安全在安全周招呼您内!

    连续五年参展的腾讯安全,今年首次以“产业数字化升级的安全战略官”形象来到展区现场,不仅鼓捣来了压箱底的安全产品矩阵,还设置了各式各样的小游戏与热乎的天津朋友们见...

    腾讯安全

扫码关注云+社区

领取腾讯云代金券