Warum in ArrayBlockingQueue das letzte Mitgliedsfeld in die lokale endgültige Variable kopieren?

80

In ArrayBlockingQueueallen Methoden, für die die Sperre erforderlich ist, wird sie finalvor dem Aufruf in eine lokale Variable kopiert 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();
    }
}

Gibt es einen Grund, this.lockin eine lokale Variable zu kopieren , lockwenn das Feld this.lockist final?

Darüber hinaus wird eine lokale Kopie von verwendet, E[]bevor darauf reagiert wird:

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

Gibt es einen Grund, ein endgültiges Feld in eine lokale endgültige Variable zu kopieren?

mjlee
quelle

Antworten:

66

Es ist eine extreme Optimierung, die Doug Lea, der Autor der Klasse, gerne verwendet. Hier ist ein Beitrag zu einem aktuellen Thread in der Mailingliste von core-libs-dev zu diesem Thema, der Ihre Frage ziemlich gut beantwortet.

von der Post:

... beim Kopieren auf Einheimische wird der kleinste Bytecode erzeugt, und für Code auf niedriger Ebene ist es schön, Code zu schreiben, der etwas näher an der Maschine liegt

ColinD
quelle
15
Starke Betonung auf "extrem"! Dies ist keine allgemeine gute Programmierpraxis, die jeder emulieren sollte.
Kevin Bourrillion
15
Zufällige Information: In einigen anderen Fällen, wenn Sie dies sehen, liegt dies daran, dass das betreffende Feld volatil ist und die Methode sicherstellen muss, dass es durchgehend einen einzigen konsistenten Wert oder eine einheitliche Referenz dafür hat.
Kevin Bourrillion
2
Ich werde diese "extreme" Optimierung in einer Kernklasse wie dieser durchführen.
Erick Robertson
4
@zamza, lokale endgültige Variablen werden nur vom Java-Compiler verwendet, nicht der Bytecode (dh JVM weiß nicht, ob eine lokale Variable endgültig ist)
bestsss
1
Ist dies neben der Bytecode-Größe auch eine Optimierung für die Ausführungsgeschwindigkeit?
SantiBailors
11

Dieser Thread gibt einige Antworten. Das Wesentliche:

  • Der Compiler kann nicht leicht beweisen, dass sich ein endgültiges Feld innerhalb einer Methode nicht ändert (aufgrund von Reflexion / Serialisierung usw.).
  • Die meisten aktuellen Compiler versuchen es tatsächlich nicht und müssten daher das letzte Feld jedes Mal neu laden, wenn es verwendet wird, was zu einem Cache-Fehler oder einem Seitenfehler führen könnte
  • Durch das Speichern in einer lokalen Variablen wird die JVM gezwungen, nur eine Last auszuführen
Assylien
quelle
2
Ich denke nicht, dass eine finalVariable von der JVM neu geladen werden muss. Wenn Sie eine finalVariable über Reflection ändern , verlieren Sie die Garantie, dass Ihr Programm ordnungsgemäß funktioniert (was bedeutet, dass der neue Wert möglicherweise nicht in allen Fällen berücksichtigt wird).
icza