Hotspot 偏向锁BiasedLocking 源码解析

目录1、定义2、init3、init方法补充说明4、revoke_bias5、bulk_revoke_or_rebias_at_safepoint6、revoke_and_rebias7、VM_RevokeBias /VM_BulkRevokeBias8、revoke_at_safepoint /revoke9、preserve_marks /r...
摘要由CSDN通过智能技术生成

     目录

1、定义

2、init

3、init方法补充说明

4、revoke_bias

5、bulk_revoke_or_rebias_at_safepoint

6、revoke_and_rebias

7、VM_RevokeBias / VM_BulkRevokeBias

8、revoke_at_safepoint / revoke

9、preserve_marks / restore_marks

10、MacroAssembler::biased_locking_enter / biased_locking_exit


     之前的博客中已经讲解了synchronized关键字的底层实现,其中涉及偏向锁的逻辑都封装在BiasedLocking中,本篇博客就详细探讨该类的实现。

1、定义

     BiasedLocking的定义位于hotspot\src\share\vm\runtime\biasedLocking.hpp中,该类定义的属性和方法都是静态的,其中属性只有一个,如下图:

BiasedLockingCounters的定义在同一个文件中,就是一个保存各项跟偏向锁有关的统计数据的数据结构,如下图:

这些统计数据主要是为了打印日志使用,如果PrintBiasedLockingStatistics为true就会打印该数据结构中的数据,默认为false。参考获取_total_entry_count内存地址的total_entry_count_addr方法的调用链,如下:

其中MacroAssembler::fast_lock和对应的MacroAssembler::fast_unlock是C2即时编译器使用的加锁和解锁的方法。

除此之外,还有一个比较关键的枚举Condition,用来描述当前偏向锁的状态,其定义如下:

NOT_BIASED表示该对象没有持有偏向锁,BIAS_REVOKED表示该对象的偏向锁已经被撤销了,即其对象头已经恢复成默认的不开启偏向锁时的状态,BIAS_REVOKED_AND_REBIASED表示当前线程重新获取了该偏向锁。

2、init

     init方法用于初始化BiasedLocking,该方法是在JVM启动时调用的,其调用链如下:

其实现如下:

void BiasedLocking::init() {
  if (UseBiasedLocking) {
    //UseBiasedLocking默认为true,表示是否使用偏向锁
    if (BiasedLockingStartupDelay > 0) {
      //BiasedLockingStartupDelay表示BiasedLocking初始化时的延时,默认为4000,避免在JVM启动时初始化,可加快JVM的启动
      EnableBiasedLockingTask* task = new EnableBiasedLockingTask(BiasedLockingStartupDelay);
      task->enroll();
    } else {
      VM_EnableBiasedLocking op(false);
      VMThread::execute(&op);
    }
  }
}

 EnableBiasedLockingTask中执行任务的实现task方法跟else分支基本一样,如下:

就构造函数的参数不同,一个是false,一个是true,该参数决定了VM_EnableBiasedLocking的执行模式,其实现如下:

class VM_EnableBiasedLocking: public VM_Operation {
 private:
  bool _is_cheap_allocated;
 public:
  VM_EnableBiasedLocking(bool is_cheap_allocated) { _is_cheap_allocated = is_cheap_allocated; }
  VMOp_Type type() const          { return VMOp_EnableBiasedLocking; }
  Mode evaluation_mode() const    { return _is_cheap_allocated ? _async_safepoint : _safepoint; }
  bool is_cheap_allocated() const { return _is_cheap_allocated; }

  void doit() {
    //将所有通过SystemDictionary加载过的类的对象头标记成其支持偏向
    SystemDictionary::classes_do(enable_biased_locking);
    //_biased_locking_enabled是一个静态static变量
    _biased_locking_enabled = true;

    if (TraceBiasedLocking) {
      tty->print_cr("Biased locking enabled");
    }
  }

  bool allow_nested_vm_operations() const        { return false; }
};

static void enable_biased_locking(Klass* k) {
  //prototype_header表示该Klass对应oop的初始对象头
  k->set_prototype_header(markOopDesc::biased_locking_prototype());
}

static markOop biased_locking_prototype() {
    //biased_lock_pattern表示该对象支持偏向锁
    return markOop( biased_lock_pattern );
  }

void SystemDictionary::classes_do(void f(Klass*)) {
  dictionary()->classes_do(f);
}

void Dictionary::classes_do(void f(Klass*)) {
  for (int index = 0; index < table_size(); index++) {
    for (DictionaryEntry* probe = bucket(index);
                          probe != NULL;
                          probe = probe->next()) {
      Klass* k = probe->klass();
      if (probe->loader_data() == InstanceKlass::cast(k)->class_loader_data()) {
        f(k);
      }
    }
  }
}

3、init方法补充说明

  理解init方法的逻辑需要知道以下几点:

1)、Klass的prototype_header的用途,其调用链如下:

其中must_be开头的三个方法用于判断该对象头是否需要保存下来,oopDesc::init_mark用于初始化对象头,将其设置为对应Klass的prototype_header,prototype_header_offset方法返回prototype_header的偏移量,主要给解释器和编译器生成Stub使用,同样用于获取Klass的prototype_header将其作为一个新创建对象的对象头,其调用链如下:

2) 枚举biased_lock_pattern的含义

biased_lock_pattern仅仅只是表示JVM已经开启了偏向锁,当前对象支持偏向锁,具体是否持有偏向锁需要通过biased_locker方法来判断,其实现如下:

即判断用来存储持有当前对象偏向锁的线程指针是否为空,为空则表示该对象的偏向锁未被其他线程占有,不为空则表示已经被其他线程占用了。 

与biased_locking_prototype方法相对的就是prototype方法,该方法返回默认的即不开启偏向锁时的对象头,其实现如下:

no_hash_in_place和no_lock_in_place都是枚举值,其定义如下:

no_hash_in_place表示未初始化hash码,unlocked_value表示当前对象没有持有任何锁,也不支持偏向锁,此时对象头的状态就是neutral,参考 is_neutral方法的实现,如下:

3) Dictionary::classes_do

       Dictionary::classes_do用于遍历所有已加载的Klass,那这个Klass是哪来的了?参考与之对应的add_klass方法实现,如下:

void Dictionary::add_klass(Symbol* class_name, ClassLoaderData* loader_data,
                           KlassHandle obj) {
  assert_locked_or_safepoint(SystemDictionary_lock);
  assert(obj() != NULL, "adding NULL obj");
  assert(obj()->name() == class_name, "sanity check on name");
  assert(loader_data != NULL, "Must be non-NULL");
  //计算hash值,Dictionary用于保存DictionaryEntry的数据结构跟Java中的HashMap是一样的
  unsigned int hash = compute_hash(class_name, loader_data);
  //计算保存该Klass的数组索引
  int index = hash_to_index(hash);
  //创建一个新的key-value键值对对象
  DictionaryEntry* entry = new_entry(hash, obj(), loader_data);
  //将其加入到数组中
  add_entry(index, entry);
}

该方法的调用链如下:

其最上层的调用方法resolve_or_null和resolve_array_class_or_null都是用来解析常量池中的Class类型的符号引用,当某个类执行某个方法时,会判断该方法字节码所用到的符号引用是否解析完成,如果未解析则调用上述resolve方法完成解析,即会加载该类,创建对应的Klass,创建完成后就会调用add_klass方法将创建的Klass加入到Dictionary中管理。可参考《Hotspot 类加载、链接和初始化 C++源码解析》

4)静态属性_biased_locking_enabled

     _biased_locking_enabled默认为false,如果UseBiasedLocking为true,则在init方法中会将其置为true,其调用链如下:

enable方法直接返回该属性,如下:

update_dictionary方法中的调用如下:

在调用add_class方法前会先执行上述逻辑,跟上面的enable_biased_locking方法一样,即将_biased_locking_enabled置为true以后会保证后面新加载的Klass的prototype_header都是biased_locking_prototype,而在此之前已加载的Klass已经通过init方法做了同样处理,如此就可以保证后续新创建的锁对象oop都可以支持偏向锁,但是之前已经创建的锁对象oop则不支持偏向锁。

4、revoke_bias

      revoke_bias的返回值只有两种NOT_BIASED和BIAS_REVOKED,前者表示目标对象没有偏向锁,后者表示已经成功撤销了该对象的偏向锁,即该方法就是用于撤销偏向锁。该方法的参数is_bulk无实际意义,只用于判断是否打印日志;参数allow_rebias用于判断是否彻底撤销偏向锁,如果为true将只是清除偏向锁中的线程指针,将其变成一个匿名的即没有被其他线程占用的偏向锁;如果为false则将其恢复成无锁状态。在撤销偏向锁时,如果是匿名偏向锁或者持有该锁的线程已经退出了或者持有该锁的线程中没有对应的BasicObjectLock(即占用该锁的方法或者代码块已经执行完了)则按照参数allow_rebias撤销偏向锁;如果有某个线程正在持有该偏向锁,无论是否当前线程,都需要将其持有的偏向锁膨胀成轻量级锁,且需要考虑锁嵌套的情形。

static BiasedLocking::Condition revoke_bias(oop o
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值