„Wenn ein Arbeiter seine Arbeit gut machen will, muss er zuerst seine Werkzeuge schärfen.“ – Konfuzius, „Die Gespräche des Konfuzius. Lu Linggong“
Titelseite > Programmierung > Grundlegendes zu JVM-Sperroptimierungen

Grundlegendes zu JVM-Sperroptimierungen

Veröffentlicht am 08.11.2024
Durchsuche:254

Understanding JVM Lock Optimizations

Parallelität ist sehr wichtig für die Entwicklung robuster, skalierbarer Anwendungen, die mehrere gleichzeitige Vorgänge ausführen können. Allerdings muss hierfür ein Preis in Bezug auf die Synchronisierung gezahlt werden. Aufgrund der damit verbundenen Gemeinkosten für den Erwerb und die Freigabe von Sperren fallen Leistungskosten an. Um diese Leistungseinbußen zu verringern, wurden mehrere Optimierungen in verschiedenen Varianten in die JVM integriert, z. B. voreingenommenes Sperren, Sperrenbeseitigung, Sperrenvergröberung und das Konzept von leichten und schweren Sperren.

In diesem Artikel sehen wir uns diese Optimierungen genauer an und gehen darauf ein, wie sie die Synchronisierung in Multithread-Java-Anwendungen verbessern.

Grundlagen der Java-Sperre

In Java stellt die Synchronisation von Blöcken oder Methoden sicher, dass jeweils nur ein Thread einen kritischen Codeabschnitt ausführen kann. Dies ist insbesondere dann wichtig, wenn die gemeinsame Nutzung von Ressourcen innerhalb der Multithread-Umgebung in Betracht gezogen wird. Java implementiert dies, indem es sich auf intrinsische Sperren verlässt – manchmal werden sie auch als Monitore bezeichnet, die Objekten oder Klassen zugeordnet sind und dabei helfen, den Zugriff auf die Threads mithilfe der synchronisierten Blöcke zu verwalten.

Obwohl die Synchronisierung eine Notwendigkeit für die Thread-Sicherheit ist, kann sie recht teuer sein, wenn die Konkurrenz gering ist oder überhaupt nicht vorhanden ist. Hier kommen JVM-Optimierungen ins Spiel. Dadurch werden die Sperrkosten gesenkt und die Gesamtleistung verbessert.

1. Voreingenommenes Sperren

Was ist Biased Locking?

Biased Locking ist eine Optimierung, die auf die Reduzierung des Overheads bei der Sperrenerfassung abzielt. Es ist optimiert, um die Kosten für den Erwerb von Sperren zu senken, die von einem einzelnen Thread dominiert werden oder auf die größtenteils von einem einzelnen Thread zugegriffen wird. Solche Programme erwerben und geben Sperren häufig durch denselben Thread frei, ohne dass andere Threads darauf einwirken. Die JVM kann dieses Muster erkennen und richtet die Sperre auf diesen bestimmten Thread aus. Der folgende Schlosserwerb ist nahezu kostenlos.

Wie funktioniert Biased Locking?

Wenn die voreingenommene Sperrung aktiviert ist, wird die Sperre beim ersten Erlangen einer Sperre durch einen Thread auf diesen Thread ausgerichtet. Die Identität des Threads wird im Header des Sperrobjekts aufgezeichnet, und nachfolgende Sperrenerlangungen durch diesen Thread erfordern keinerlei Synchronisierung – sie prüfen lediglich, ob die Sperre auf den aktuellen Thread ausgerichtet ist, was ein sehr schneller, nicht blockierender Vorgang ist .

Wenn ein anderer Thread versucht, die Sperre zu erhalten, wird die Voreingenommenheit aufgehoben und JVM greift auf einen standardmäßigen unvoreingenommenen Sperrmechanismus zurück. Zu diesem Zeitpunkt handelt es sich nun um eine Standardsperre, und der zweite Thread muss sie über einen Standardsperrvorgang erwerben.
Vorteile von Biased Locking

Leistung: Der Erwerb desselben Threads auf einer voreingenommenen Sperre ist fast ein kostenloser Sperrenerwerb.

Daher ist eine Konfliktbehandlung nicht erforderlich, da andere Threads keine Chance haben, an der Erlangung der Sperre beteiligt zu sein.

Geringerer Overhead: Der Status der Sperre muss sich nicht ändern oder synchronisierungsbezogene Metadaten müssen geändert werden, außer im Konfliktfall. 
 

Wann wird Biased Locking verwendet?

Voreingenommenes Sperren ist in Anwendungen nützlich, in denen auf Sperren hauptsächlich durch denselben Thread zugegriffen wird, z. B. Single-Thread-Anwendungen oder eine Anwendung, die unter Multithreading nur geringe Sperrenkonflikte aufweist. Es ist in den meisten JVMs standardmäßig aktiviert.

So deaktivieren Sie die voreingenommene Sperrung

Biased Locking ist standardmäßig aktiviert, kann aber auch mit dem JVM-Flag wie unten deaktiviert werden:

-XX:-UseBiasedLocking

2. Sperrenbeseitigung

Was ist Sperrenbeseitigung?

Die Eliminierung von Sperren ist eine sehr leistungsstarke Optimierung, bei der die JVM einige unnötige Synchronisierungen (Sperren) vollständig eliminiert. Während der JIT-Kompilierung wird der Code auf mögliche Möglichkeiten überprüft und dabei festgestellt, dass keine Synchronisierung erforderlich ist. Dies tritt normalerweise auf, wenn nur ein Thread auf die Sperre zugegriffen hat oder das Objekt, das die JVM zum Synchronisieren verwendet, nicht dasselbe Objekt in verschiedenen Threads verwendet. Sobald die JVM feststellt, dass sie nicht mehr erforderlich ist, hebt sie die Sperre auf.

Wie funktioniert die Sperrenbeseitigung?

In der Escape-Analysephase der JIT-Kompilierung prüft JVM, ob das Objekt auf einen einzelnen Thread beschränkt ist oder nur in einem lokalen Kontext verwendet wird. Wenn die Synchronisierung für dieses Objekt entfernt werden kann, weil ein Objekt den Bereich des Threads, der es erstellt hat, nicht verlässt, ist dies der Fall.

Wenn beispielsweise ein Objekt vollständig innerhalb einer Methode erstellt und verwendet wird (und nicht von mehreren Threads gemeinsam genutzt wird), erkennt die JVM, dass möglicherweise kein anderer Thread auf das Objekt zugreifen kann und dass daher die gesamte Synchronisierung redundant ist. In einem solchen Fall hebt der JIT-Compiler die Sperre einfach vollständig auf.

Kein Sperraufwand: Durch die Eliminierung unnötiger Synchronisierung wird auch verhindert, dass die JVM die Kosten für den Erwerb und die Freigabe von Sperren überhaupt trägt.

Höherer Durchsatz: Dead Synch kann manchmal zu einem höheren Durchsatz der Anwendung führen, insbesondere wenn der Code viele synchronisierte Blöcke enthält.

Sehen Sie sich diesen Code an:

public void someMethod() {
    StringBuilder sb = new StringBuilder();
    synchronized (sb) {
        sb.append("Hello");
        sb.append("World");
    }
}

In diesem Fall ist eine Synchronisierung auf sb nicht erforderlich, da der StringBuilder nur innerhalb der someMethod verwendet und nicht von anderen Threads gemeinsam genutzt wird. Auf diese Weise kann die JVM eine Escape-Analyse durchführen, um die Sperre aufzuheben.

3. Vergröberung sperren

Was ist Lock Coarsening?

Die Vergröberung von Sperren ist eine Optimierung, bei der die JVM den Umfang einer Sperre erweitert, um mehr Codeblöcke abzudecken, anstatt die Sperre kontinuierlich in Schleifen oder kleinen Codeabschnitten zu erwerben und freizugeben.

Vergröberungsarbeit sperren

Wenn die JVM feststellt, dass eine enge Schleife oder mehrere benachbarte Codeblöcke zu häufig eine Sperre erwerben und freigeben, kann sie die Sperre vergröbern, indem sie die Sperre außerhalb der Schleife oder über mehrere Codeblöcke hinweg übernimmt. Dies macht den wiederholten Erwerb und die Freigabe der Sperre kostspielig und ermöglicht es einem Thread, die Sperre für weitere Iterationen zu halten.

Codebeispiel: Vergröberung sperren

Bedenken Sie dieses Code-Snippet:

for (int i = 0; i 



Durch die Sperrenvergröberung wird die Sperrenerfassung außerhalb der Schleife verschoben, sodass der Thread die Sperre nur einmal erhält:

synchronized (lock) {
  for (int i = 0; i 



Die JVM kann die Leistung erheblich verbessern, indem sie mehr Erfassungen und Freigaben der Sperre vermeidet.

Vergröberungsvorteile sperren

Geringere Freiheit beim Sperren-Overhead: Durch die Vergröberung werden Sperrerfassungen und -freigaben vermieden, insbesondere in Hotspot-Code, wie z. B. Schleifen, die tausende Male wiederholt wurden.

Verbesserte Leistung:
Das Sperren über einen längeren Zeitraum verbessert die Leistung im Vergleich zu dem Szenario, in dem ohne Sperren eine solche Sperre mehrmals erworben und freigegeben würde.

4. Leichte und schwere Schlösser

Die JVM verwendet zwei verschiedene Sperrtechniken, basierend auf dem Grad der Konkurrenz zwischen den Threads. Zu diesen Techniken gehören leichte Schlösser und schwere Schlösser.

Leichte Verriegelung

Lightweight Locking findet statt, wenn keine Konfliktsperre vorhanden ist, was bedeutet, dass nur ein Thread versucht, diese Sperre zu erhalten. In solchen Szenarien optimiert die JVM die Erfassung mithilfe einer CAS-Operation, wenn versucht wird, die Sperre zu erhalten, was ohne schwere Synchronisierung erfolgen kann.

Schwergewichtsverriegelung

Falls mehrere Threads dieselbe Sperre erhalten möchten; Das heißt, es liegt ein Konflikt vor, und die JVM eskaliert diesen zu einer schweren Sperrung. Dies würde das Blockieren von Threads auf Betriebssystemebene und deren Verwaltung mithilfe von Synchronisierungsprimitiven auf Betriebssystemebene beinhalten. Schwere Sperren sind langsamer, da sie tatsächlich erfordern, dass das Betriebssystem den Kontextwechsel durchführt und Threads verwaltet.

Eskalation sperren

Wenn bei einer Lightweight-Sperre ein Konflikt auftritt, kann die JVM ihn zu einer Heavyweight-Sperre eskalieren. Eskalation bedeutet hier den Wechsel von der schnellen Sperre auf Benutzerebene zu einer teureren Sperre auf Betriebssystemebene, die Thread-Blockierung umfasst.

Vorteile von Lightweight-Schlössern

Schnelle Erfassung einer Sperre: Wenn es keinen Konflikt gibt, sind Lightweight-Sperren viel schneller als Heavyweight-Sperren, da sie eine Synchronisierung auf Betriebssystemebene vermeiden.

Reduzierte Blockierung: Ohne Konflikte blockieren Threads nicht und nehmen linear mit geringerer Latenz zu.

Nachteile von Schwergewichtsschlössern

Leistungsaufwand: Schwere Sperren verursachen Kosten für Thread-Blockierung, Kontextwechsel und das Aufwecken von Threads mit Leistungseinbußen bei sehr hohen Konfliktregimen.

All diese Optimierungen helfen der JVM, die Leistung in Multithread-Anwendungen zu verbessern, sodass Entwickler jetzt sicheren, gleichzeitigen Code schreiben können, ohne große Einbußen beim Synchronisierungsaufwand hinnehmen zu müssen. Das Verständnis dieser Optimierungen kann Entwicklern dabei helfen, effizientere Systeme zu entwerfen, insbesondere in Fällen, in denen das Sperren hohe Leistungseinbußen mit sich bringt.

Freigabeerklärung Dieser Artikel ist abgedruckt unter: https://dev.to/arashariani/understanding-jvm-lock-optimizations-4l5i?1 Bei Verstößen wenden Sie sich bitte an [email protected], um ihn zu löschen
Neuestes Tutorial Mehr>

Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.

Copyright© 2022 湘ICP备2022001581号-3