詳細なOpenJDKソースコード-バイアスロックの遅延効果を実装する方法

元に戻す、再バイアスを含むバイアスロックについては、以前にソースコードを分析したことがあるので、ここでは詳しく説明せず、主に発効の遅れの問題について説明します。さらに、この記事に掲載されているソースコードのほとんどは、現在懸念されているロジックのみを保持しています。

  仮想マシンがパラメータ-XX:+ UseBiasedLockingを提供してバイアスロックの最適化をオンまたはオフにすることはわかっていますが(デフォルトはオン)、バイアスロックのアクティブ化にはデフォルトの遅延時間があります。パラメータ-XX:BiasedLockingStartupDelay、デフォルトは4秒、デフォルト設定はglobals.hppにあります。

  product(intx, BiasedLockingStartupDelay, 4000,"Number of milliseconds to wait before enabling biased locking")

  この記事では、この遅延がどのように実装されているかを確認するためにソースコードレベルに移動します。あまり意味がないので、トピックに直接進みましょう〜

ソースコード分析

  バイアスされたロックの初期化ロジックを確認するには、biasedLocking.cppと入力します。

void BiasedLocking::init() {
    
    
  if (UseBiasedLocking) {
    
    
  	//启用偏向锁
    if (BiasedLockingStartupDelay > 0) {
    
    
	  //需要延时生效,默认4000ms
      EnableBiasedLockingTask* task = new EnableBiasedLockingTask(BiasedLockingStartupDelay);
      //调用enroll注册任务
      task->enroll();
    } else {
    
    
      //否则立即调用VM_EnableBiasedLocking 开启偏向锁
      VM_EnableBiasedLocking op(false);
      VMThread::execute(&op);
    }
  }
}

  まず、UseBiasedLockingがtrueであり、バイアスロックがオンになっていることを示します。BiasedLockingStartupDelayが0より大きい場合は、有効にするために遅延が必要であることを意味します。これがEnableBiasedLockingTaskです。このタスクの定義を見てみましょう。

class EnableBiasedLockingTask : public PeriodicTask {
    
    
 public:
  EnableBiasedLockingTask(size_t interval_time) : PeriodicTask(interval_time) {
    
    }
  //这个方法这时先不管,后面调用的时候再说
  virtual void task() {
    
    
    VM_EnableBiasedLocking *op = new VM_EnableBiasedLocking(true);
    VMThread::execute(op);
  }
};

  EnableBiasedLockingTaskはPeriodicTaskの派生クラスであることがわかります。親クラスのパラメーターコンストラクターはinterval_timeパラメーターで呼び出されます。PeriodicTaskはtask.hppで定義され、コンストラクターはtask.cppで実装されます。

PeriodicTask::PeriodicTask(size_t interval_time) :
  _counter(0), _interval((int) interval_time) {
    
    
}

  interval_timeは_interval属性に割り当てられ、_intervalはミリ秒単位のプライベート定数であり、_counterは0に設定されます。

class PeriodicTask: public CHeapObj<mtInternal> {
    
    
private:
  int _counter;
  const int _interval;
}

  これまで、EnableBiasedLockingTask(PeriodicTaskの派生クラス)が作成されていました。作成後、親クラスのPeriodicTaskで定義されているtask-> enroll()メソッドが呼び出されます。

void enroll();

  task.cppでこのメソッドの実装を見つけてください。

void PeriodicTask::enroll() {
    
    
  MutexLockerEx ml(PeriodicTask_lock->owned_by_self() ?
                     NULL : PeriodicTask_lock);
  _tasks[_num_tasks++] = this;
  WatcherThread* thread = WatcherThread::watcher_thread();
  if (thread) {
    
    
    thread->unpark();
  } else {
    
    
    WatcherThread::start();
  }
}

  WatcherThreadオブジェクトが最終的に使用されていることがわかります。WatcherThreadはThreadオブジェクトの派生クラスです。thread.hppで定義されています。ここにコードは投稿されません。それ以外の場合は逸脱します〜WatcherThread :: runに直接移動しましょう。 ()メソッド。見て、2行のコードを投稿するだけです。

void WatcherThread::run() {
    
    
	......
	int time_waited = sleep();
	......
	PeriodicTask::real_time_tick(time_waited);
	......
}

  次に、PeriodicTask :: real_time_tick(time_waited)メソッドの実装を見てみましょう。コアコードは次のとおりです。

for(int index = 0; index < _num_tasks; index++) {
    
    
	 _tasks[index]->execute_if_pending(delay_time);
}

  以前、PeriodicTask :: enroll()メソッドを呼び出してEnableBiasedLockingTaskを_tasks配列に登録しました。ここでは、_tasksからタスクをトラバースし、親クラスのPeriodicTaskで定義されているexecute_if_pendingメソッドをそれぞれ呼び出します。

 void execute_if_pending(int delay_time) {
    
    
    // make sure we don't overflow
    jlong tmp = (jlong) _counter + (jlong) delay_time;
    if (tmp >= (jlong) _interval) {
    
    
      _counter = 0;
      task();
    } else {
    
    
      _counter += delay_time;
    }
  }

  判定が_interval時間に達すると、task()メソッドが呼び出されます。task()メソッドはEnableBiasedLockingTaskメソッドで定義されているため、回避してEnableBiasedLockingTaskに戻ります。

class EnableBiasedLockingTask : public PeriodicTask {
    
    
 public:
  EnableBiasedLockingTask(size_t interval_time) : PeriodicTask(interval_time) {
    
    }

  virtual void task() {
    
    
    //实际上是一个VM_Operation
    VM_EnableBiasedLocking *op = new VM_EnableBiasedLocking(true);
    VMThread::execute(op);
  }
};

  タスクメソッドのロジックは比較的単純です。最初に、VM_EnableBiasedLockingオブジェクトが生成されます。これはVM_Operationクラスの派生クラスでありdoitメソッドのロジックを実装し、VMThread :: executeを介してこの操作を実行します(構成が遅延して有効にならない場合、このロジックはバイアスロックが初期化)、そして最後にdoit()メソッド:

class VM_EnableBiasedLocking: public VM_Operation {
    
    
 public:
  void doit() {
    
    
    SystemDictionary::classes_do(enable_biased_locking);
    _biased_locking_enabled = true;
  }
};

  doitメソッドは主にSystemDictionary :: classes_doメソッドを呼び出します。後で戻ってenale_biased_lockingとは何かを確認します。次に、SystemDictionary :: classes_doメソッドの機能を見てみましょう。

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

  ここで非常に明確です。classes_doは関数ポインタを受け取り、dictionary()関数はロードされたクラスを返し、これらのロードされたクラスは、Dictionaryの形式でSystemDictionaryの静的メンバーに格納されます。

static Dictionary*            _dictionary;

  この辞書はTwoOopHashtableの派生クラスです

class Dictionary : public TwoOopHashtable<Klass*, mtClass> {
    
    
}

  ここでは、このデータ構造についてあまり気にする必要はありません。dictionary()関数がロードされたクラスを返し、これらのクラスで渡された関数を呼び出すことを知っておく必要があります。辞書.cppに実装されているclasses_do関数:

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);
      }
    }
  }
}

  ロードされたすべてのクラスをトラバースしてから、渡された関数を呼び出し、トラバースされた各Klassをパラメーターとして渡します。この時点で、VM_EnableBiasedLockingのdoit()メソッドに戻ることができます。つまり、次のコード行を見てください。

SystemDictionary::classes_do(enable_biased_locking);

  前の分析の後、SystemDictionary :: classes_doが関数ポインターを受け取り、ロードされたすべてのクラスをトラバースし、入力パラメーターとしてクラス(Klass)を使用して関数を呼び出すことがわかりました。次に、このenable_biased_lockingは関数ポインターである必要があります。実際、biaseLocking.cppに戻って、この関数の定義を見つけます。

static void enable_biased_locking(Klass* k) {
    
    
  k->set_prototype_header(markOopDesc::biased_locking_prototype());
}

  ここをトレースすると、すべてが明確になります。KlassにはmarkOoptype _prototype_headerプロパティがあります。これは、Klass.hppで定義されているオブジェクトバイアスロックの有効化/無効化を示すために使用できます。

class Klass : public Metadata {
    
    
 protected:
 	markOop  _prototype_header;
}

  set_prototype_header()メソッドがインラインメソッドである場合、特定の実装はklass.inline.hppにあります。

inline void Klass::set_prototype_header(markOop header) {
    
    
  _prototype_header = header;
}

  ご覧のとおり、_prototype_headerプロパティは指定されたmarkOopに設定されているので、このmarkOopは正確には何ですか?一部の友人がすでになじみのある感覚を持っているかどうかはわかりません〜biaseLocking.cppのenable_biased_lockingメソッドにもう一度戻ります。

static void enable_biased_locking(Klass* k) {
    
    
  k->set_prototype_header(markOopDesc::biased_locking_prototype());
}

  markOopDesc :: biased_locking_prototype()によって返される特定のmarkOopを見てみましょう。markOop.hに移動し、biased_locking_prototypeメソッドを見つけます。

static markOop biased_locking_prototype() {
    
    
    return markOop( biased_lock_pattern );
  }

  biased_lock_patternの定義を見てください。

  enum {
    
     locked_value             = 0,
         unlocked_value           = 1,
         monitor_value            = 2,
         marked_value             = 3,
         biased_lock_pattern      = 5
  };

  これは見覚えがありますか?慣れていない場合は、メモを追加してもう一度見てみましょう。

 enum {
    
      locked_value             = 0,//00 轻量级锁
         unlocked_value           = 1,//01 无锁
         monitor_value            = 2,//10 重量级锁
         marked_value             = 3,//11 GC标记
         biased_lock_pattern      = 5 //101 偏向锁,1位偏向标记和2位状态标记(01)
  };

  そうです、この定義は、オブジェクトヘッダーのマークワードのロックステータス識別子です。

総括する

ここに画像の説明を挿入
  では、なぜ仮想マシンがデフォルトの遅延効果制御をバイアスロックに追加するのでしょうか。仮想マシンは、起動プロセス中に一部のスレッドも開始し、一部のロジックも内部で制御されます。バイアスロックを直接オンにすると、通常、バイアスが取り消されます。JVMは、バイアスの取り消しを有効にするときに多くのことを使用します。 。安全のために、最初にバイアスロックを有効にしないことは、JVMの起動速度を向上させるのに役立ちます(これはo(╯□╰)oである必要があるように感じます)。

この記事はブロガーの個人的な理解に基づいています。エラーがある場合は、指摘していただきありがとうございます。

おすすめ

転載: blog.csdn.net/huangzhilin2015/article/details/115314096
おすすめ