Heim >类库下载 >java类库 >Optimierung der Java-Sperre

Optimierung der Java-Sperre

高洛峰
高洛峰Original
2016-10-15 13:55:551618Durchsuche

1. Prinzip der Synchronisation

Die JVM-Spezifikation legt fest, dass die JVM die Methodensynchronisation und die Codeblocksynchronisation basierend auf dem Betreten und Verlassen des Monitorobjekts implementiert, die Implementierungsdetails der beiden sind jedoch unterschiedlich. Die Codeblocksynchronisierung wird mithilfe der Anweisungen „monitorenter“ und „monitorexit“ implementiert, während die Methodensynchronisierung mithilfe einer anderen Methode implementiert wird. Die Details sind in der JVM-Spezifikation nicht angegeben, die Methodensynchronisierung kann jedoch auch mithilfe dieser beiden Anweisungen implementiert werden. Die Monitorenter-Anweisung wird nach der Kompilierung am Anfang des synchronisierten Codeblocks eingefügt, während der Monitorexit am Ende der Methode und der Ausnahme eingefügt wird. Die JVM muss sicherstellen, dass jeder Monitorenter mit einem entsprechenden Monitorexit gepaart werden muss. Jedem Objekt ist ein Monitor zugeordnet. Wenn ein Monitor gehalten wird, befindet er sich in einem gesperrten Zustand. Wenn der Thread die Monitorenter-Anweisung ausführt, versucht er, den Besitz des dem Objekt entsprechenden Monitors zu erlangen, dh er versucht, die Sperre des Objekts zu erhalten.

2. Java-Objekt-Header

ist im Java-Objekt-Header gesperrt. Wenn es sich bei dem Objekt um einen Array-Typ handelt, verwendet die virtuelle Maschine 3 Wörter (Wortbreite) zum Speichern des Objekt-Headers. Wenn es sich bei dem Objekt um einen Nicht-Array-Typ handelt, verwendet die virtuelle Maschine 2 Wörter (Wortbreite) zum Speichern des Objekt-Headers. In einer virtuellen 32-Bit-Maschine entspricht eine Wortbreite vier Bytes, also 32 Bit.

Optimierung der Java-Sperre

Das Markierungswort im Java-Objekt-Header speichert standardmäßig den HashCode, das Generationsalter und das Sperrmarkierungsbit des Objekts. Die Standardspeicherstruktur von Mark Word für 32-Bit-JVM lautet wie folgt:

Optimierung der Java-Sperre

Während des Betriebs ändern sich die in Mark Word gespeicherten Daten, wenn sich das Sperrflag ändert. Mark Word kann sich ändern, um die folgenden vier Arten von Daten zu speichern:

Optimierung der Java-Sperre

3. Mehrere Arten von Sperren

Das Blockieren und Aufwecken von Threads erfordert die CPU Aus dem Benutzermodus wechseln Als Kernzustand stellt häufiges Blockieren und Aufwachen eine starke Belastung für die CPU dar.

Um den durch den Erwerb und die Freigabe von Sperren verursachten Leistungsverbrauch zu verringern, hat Java SE1.6 „voreingenommene Sperren“ und „leichte Sperren“ eingeführt. Daher gibt es im Java SE1.6-Status vier Arten von Sperren: Sperren -freier Status, voreingenommener Sperrstatus, leichter Sperrstatus und schwerer Sperrstatus, der mit der Wettbewerbssituation allmählich eskaliert.

Sperren können hochgestuft, aber nicht herabgestuft werden, was bedeutet, dass eine voreingenommene Sperre nach dem Upgrade auf eine leichte Sperre nicht auf eine voreingenommene Sperre herabgestuft werden kann. Der Zweck dieser Strategie des Sperren-Upgrades, aber nicht des Downgrades besteht darin, die Effizienz beim Erwerb und Freigeben von Sperren zu verbessern.

3.1 Voreingenommene Sperren

Der Autor von Hotspot hat durch frühere Untersuchungen herausgefunden, dass Sperren in den meisten Fällen nicht nur keine Multi-Thread-Konkurrenz haben, sondern immer mehrmals von demselben Thread erworben werden. Der Zweck des voreingenommenen Sperrens besteht darin, den Overhead des Lock Reentrancy (CAS) für einen Thread zu eliminieren, nachdem er die Sperre erhalten hat, was den Thread scheinbar schützt.

Optimierung der Java-Sperre

Weiteres Verständnis von voreingenommenen Sperren

Sie müssen nichts tun, um die voreingenommene Sperre aufzuheben, was bedeutet, dass der MarkValue zur voreingenommenen Sperre hinzugefügt wird wird immer beibehalten. Der Zustand der Sperre ist voreingenommen, sodass kein Overhead entsteht, selbst wenn derselbe Thread kontinuierlich gesperrt und entsperrt wird.

Andererseits werden voreingenommene Sperren eher beendet als leichte Sperren, wenn es zu Sperrenkonkurrenz kommt, während allgemeine voreingenommene Sperren zu leichten Sperren hochgestuft werden, wenn unterschiedliche Threads für Sperren gelten Dies bedeutet, dass, wenn ein Objekt zuerst von Thread 1 gesperrt und entsperrt wird und dann von Thread 2 gesperrt und entsperrt wird, kein Sperrkonflikt im Prozess vorliegt und auch ein voreingenommener Sperrfehler auftritt. Der Unterschied besteht darin, dass er dieses Mal zuerst auftreten muss Degenerieren Sie zu „Keine“. Sperrstatus plus leichte Sperre, wie in der Abbildung gezeigt:

Optimierung der Java-Sperre

Darüber hinaus führt die JVM dies auch für Situationen aus, in denen eine Multithread-Sperre vorhanden ist Keine Sperrkonkurrenz. Die Optimierung klingt umständlich, aber diese Situation kann in realen Anwendungen tatsächlich auftreten, da zusätzlich zum gegenseitigen Ausschluss auch Synchronisationsbeziehungen zwischen den beiden Threads (einer vorne und einer dahinter) auftreten können Die Beziehung zum gemeinsamen Objekt ist wahrscheinlich konfliktfrei. In diesem Fall verwendet die JVM eine Epoche, um einen sperrenbasierten Zeitstempel darzustellen (es ist ziemlich teuer, einen Zeitstempel tatsächlich zu generieren, daher sollte er als eine einem Zeitstempel ähnliche Kennung verstanden werden. Für die Epoche ist dies die offizielle Erklärung A). Ein ähnlicher Mechanismus, der als Massen-Rebiasing bezeichnet wird, optimiert Situationen, in denen Objekte einer Klasse von verschiedenen Threads gesperrt und entsperrt werden, jedoch niemals gleichzeitig. Er macht die Voreingenommenheit aller Instanzen einer Klasse ungültig, ohne die voreingenommene Sperrung zu deaktivieren Zeitstempel, der die Gültigkeit des Bias angibt. Die Massen-Rebiasierung kann dann effizient als Inkrement der Epoche in der entsprechenden Klasse implementiert werden gesperrt werden, erkennt der Code einen anderen Wert im Header-Wort und richtet das Objekt erneut auf den aktuellen Thread aus.

Bias Lock Acquisition

Wenn ein Thread auf einen synchronisierten Block zugreift und eine Sperre erhält, Die sperrorientierte Thread-ID wird im Objektheader und der Sperrdatensatz im Stapelrahmen gespeichert. In Zukunft muss der Thread beim Betreten und Verlassen des synchronisierten Blocks keine CAS-Operationen mehr durchführen, sondern nur Sie Sie müssen lediglich testen, ob das Mark Word im Objektheader eine Bias-Sperre speichert, die auf den aktuellen Thread verweist. Wenn der Test erfolgreich ist, bedeutet dies, dass der Thread die Sperre erhalten hat. Wenn der Test fehlschlägt, müssen Sie erneut testen Das Bias-Lock-Flag im Mark Word ist auf 1 gesetzt (was darauf hinweist, dass es sich derzeit um eine Bias-Sperre handelt. Wenn es nicht gesetzt ist, wird CAS verwendet, um um die Sperre zu konkurrieren.) CAS weist die voreingenommene Sperre des Objektheaders auf den aktuellen Thread hin.

Widerruf der voreingenommenen Sperre

Die voreingenommene Sperre verwendet einen Mechanismus, der wartet, bis ein Wettbewerb stattfindet, bevor er die Sperre aufhebt. Daher wird der Thread, der die voreingenommene Sperre hält, versuchen, um die voreingenommene Sperre zu konkurrieren wird es freigeben. Die Aufhebung der voreingenommenen Sperre erfordert das Warten auf den globalen Sicherheitspunkt (zu diesem Zeitpunkt wird kein Bytecode ausgeführt). Zuerst wird der Thread angehalten, der die voreingenommene Sperre hält, und dann wird überprüft, ob der Thread, der die voreingenommene Sperre hält, aktiv ist. Wenn der Thread nicht aktiv ist, wird der Objektheader auf einen sperrenfreien Status gesetzt. Wenn der Thread noch aktiv ist, wird der Stapel mit der voreingenommenen Sperre ausgeführt und der Sperrdatensatz des voreingenommenen Objekts durchlaufen Der Sperrdatensatz im Stapel und das Markierungswort des Objektheaders werden entweder wieder auf andere Threads voreingenommen, entweder auf sperrenfrei zurückgesetzt oder das Objekt als ungeeignet als voreingenommene Sperre markiert und schließlich der angehaltene Thread aktiviert. Thread 1 in der Abbildung unten demonstriert den Prozess der Bias-Lock-Initialisierung und Thread 2 demonstriert den Prozess der Bias-Lock-Aufhebung.

Bias-Lock-Einstellungen

Bias-Lock deaktivieren: Bias-Lock ist in Java 6 und Java 7 standardmäßig aktiviert, wird jedoch erst einige Sekunden nach dem Start der Anwendung aktiviert, ggf. mit Verzögerung kann mit dem JVM-Parameter -XX deaktiviert werden: BiasedLockingStartupDelay=0. Wenn Sie sicher sind, dass sich alle Sperren in Ihrer Anwendung normalerweise im Wettbewerbszustand befinden, können Sie die voreingenommene Sperre über den JVM-Parameter -XX:-UseBiasedLocking=false deaktivieren und dann standardmäßig in den leichten Sperrzustand wechseln.

3.2 Spin Lock

Das Blockieren und Aufwecken von Threads erfordert, dass die CPU vom Benutzermodus in den Kernmodus wechselt. Häufiges Blockieren und Aufwecken stellt eine starke Belastung für die CPU dar. Gleichzeitig können wir feststellen, dass der Sperrstatus vieler Objektsperren nur für kurze Zeit anhält, z. B. die Selbstinkrementierungsoperation einer Ganzzahl. Es lohnt sich offensichtlich nicht, den Thread zu blockieren und aufzuwecken Nach kurzer Zeit wurden Spin-Locks eingeführt.

Der sogenannte „Spin“ besteht darin, den Thread eine bedeutungslose Schleife ausführen zu lassen und dann nach dem Ende der Schleife erneut um die Sperre zu konkurrieren. Wenn es keine Konkurrenz gibt, um die Schleife fortzusetzen, wird der Thread dies immer tun im laufenden Zustand während der Schleife, aber die JVM-basierte Thread-Planung überträgt Zeitscheiben, sodass andere Threads weiterhin die Möglichkeit haben, Sperren zu beantragen und freizugeben.

Spin-Sperren sparen Zeit und Platz (Warteschlangenwartung usw.) beim Blockieren von Sperren, aber langfristige Spins werden zu „beschäftigtem Warten“, und beschäftigtes Warten ist offensichtlich nicht so gut wie das Blockieren von Sperren. Daher wird die Anzahl der Spins im Allgemeinen innerhalb eines Bereichs wie 10, 100 usw. gesteuert. Nach Überschreiten dieses Bereichs wird die Spin-Sperre zu einer Blockiersperre aufgewertet.

In Bezug auf die Auswahl des Spin-Lock-Zeitraums glaubt HotSpot, dass der beste Zeitpunkt der Zeitpunkt eines Thread-Kontextwechsels sein sollte, dies wurde jedoch bisher noch nicht durchgeführt. Nach einer Untersuchung pausiert HotSpot derzeit nur einige CPU-Zyklen während der Montage. Zusätzlich zur Spin-Zyklus-Auswahl führt HotSpot auch viele andere Spin-Optimierungsstrategien durch, wie folgt:

Wenn die durchschnittliche Auslastung geringer ist als die der CPUs, er dreht sich weiter

Wenn sich mehr als (CPUs/2) Threads drehen, werden die nachfolgenden Threads direkt blockiert

Wenn der sich drehende Thread feststellt, dass sich der Eigentümer geändert hat, wird die Spin-Zeit (Spin Anzahl) wird verzögert) oder in den Blockierungsmodus wechseln.

Das Worst-Case-Szenario für die Spin-Zeit ist die Speicherverzögerung der CPU (CPU A speichert einen Teil davon). Daten und die direkte Zeitdifferenz, bis CPU B die Daten lernt)

3.3 Leichte Sperre

Leichte Sperre

Bevor der Thread den Synchronisationsblock ausführt, erstellt die JVM zunächst Ein Thread im Stapelrahmen des aktuellen Threads speichert den Speicherplatz des Sperrdatensatzes und kopiert das Mark Word im Objektheader in den Sperrdatensatz, der offiziell als Displaced Mark Word bezeichnet wird. Der Thread versucht dann, mithilfe von CAS das Markierungswort im Objektheader durch einen Zeiger auf den Sperrdatensatz zu ersetzen. Wenn dies erfolgreich ist, erhält der aktuelle Thread die Sperre. Wenn die Spin-Erfassung immer noch fehlschlägt, bedeutet dies, dass andere Threads um die Sperre konkurrieren (zwei oder mehr Threads, die um dieselbe Sperre konkurrieren). dann werden leichte Level-Schlösser zu schweren Schlössern erweitert.

Lightweight-Lock-Entsperrung

Bei der Lightweight-Entsperrung wird die atomare CAS-Operation verwendet, um das Displaced Mark Word wieder in den Objektheader zu ersetzen. Bei Erfolg bedeutet dies, dass der Synchronisierungsprozess abgeschlossen ist . Wenn dies fehlschlägt, bedeutet dies, dass andere Threads versucht haben, die Sperre zu erlangen, und der angehaltene Thread muss beim Aufheben der Sperre aktiviert werden. Die folgende Abbildung zeigt ein Flussdiagramm, in dem zwei Threads gleichzeitig um die Sperre konkurrieren, was zu einer Sperrenerweiterung führt.

Optimierung der Java-Sperre

Optimierung der Java-Sperre

3.4 Heavyweight Lock

Weightlock wird in der JVM auch als Objektmonitor (Monitor) bezeichnet Wie Mutex in C verfügt es nicht nur über die gegenseitige Ausschlussfunktion von Mutex, sondern ist auch für die Implementierung der Semaphore-Funktion verantwortlich, was bedeutet, dass es mindestens eine Warteschlange für konkurrierende Sperren und eine Signalblockierungswarteschlange (Wartewarteschlange) enthält Ersteres ist für den gegenseitigen Ausschluss verantwortlich, letzteres wird für die Thread-Synchronisation verwendet.

4. Vergleich der Vor- und Nachteile von Schlössern

Optimierung der Java-Sperre

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn