ArrayBlockingQueueで、最終メンバーフィールドをローカルの最終変数にコピーするのはなぜですか?


81

ではArrayBlockingQueue、ロックを必要とするすべてのメソッドは、をfinal呼び出す前にロックをローカル変数にコピーしますlock()

public boolean offer(E e) {
    if (e == null) throw new NullPointerException();
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        if (count == items.length)
            return false;
        else {
            insert(e);
            return true;
        }
    } finally {
        lock.unlock();
    }
}

フィールドがであるときにthis.lockローカル変数lockにコピーする理由はありますか?this.lockfinal

さらに、E[]アクションを実行する前にのローカルコピーも使用します。

private E extract() {
    final E[] items = this.items;
    E x = items[takeIndex];
    items[takeIndex] = null;
    takeIndex = inc(takeIndex);
    --count;
    notFull.signal();
    return x;
}

最終フィールドをローカルの最終変数にコピーする理由はありますか?

回答:


68

これは、クラスの作成者であるDougLeaが使用するのが好きな極端な最適化です。これは、core-libs-devメーリングリストの最近のスレッドで、あなたの質問に非常によく答えるこの正確な主題についての投稿です。

投稿から:

...ローカルにコピーすると最小のバイトコードが生成されます。低レベルのコードの場合は、マシンに少し近いコードを作成すると便利です。


15
「極限」を重視!これは、誰もがエミュレートする必要のある汎用の優れたプログラミング手法ではありません。
Kevin Bourrillion 2010年

15
ランダムな参考:これが行われていることを確認できる他の場合は、問題のフィールドが揮発性であるためです。メソッドは、全体を通して単一の一貫した値または参照を取得する必要があります。
Kevin Bourrillion 2010年

2
このようなコアクラスでこの「極端な」最適化を行います。
エリックロバートソン

4
@zamza、ローカル最終変数は、バイトコードではなくJavaコンパイラによってのみ使用されます(つまり、JVMはローカル変数が最終であるかどうかを知りません)
bestsss 2011年

1
バイトコードサイズに加えて、これは実行速度の最適化でもありますか?
SantiBailors 2017

13

このスレッドはいくつかの答えを与えます。実質的に:

  • コンパイラは、メソッド内で最終フィールドが変更されないことを簡単に証明できません(リフレクション/シリアル化などのため)。
  • 現在のほとんどのコンパイラは実際には試行しないため、使用するたびに最終フィールドをリロードする必要があり、キャッシュミスやページフォールトが発生する可能性があります。
  • ローカル変数に格納すると、JVMは1回のロードのみを実行します。

2
final変数をJVMで再ロードする必要はないと思います。finalリフレクションを介して変数を変更すると、プログラムが正しく機能するという保証が失われます(つまり、すべての場合に新しい値が考慮されない可能性があります)。
icza 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.