Heim  >  Artikel  >  Java  >  Interview: Wissen Sie, welche Methoden zur Java-Performance-Optimierung verfügbar sind?

Interview: Wissen Sie, welche Methoden zur Java-Performance-Optimierung verfügbar sind?

Java后端技术全栈
Java后端技术全栈nach vorne
2023-08-16 16:49:14880Durchsuche

Vor zwei Tagen kam ein Freund aus der Gruppe, um mit mir zu plaudern. Während des Interviews wurde er gefragt, wie er die Methoden zur Leistungsoptimierung beantworten solle. Dieser Artikel konzentriert sich hauptsächlich auf die theoretische Analyse. Werfen wir einen allgemeinen Blick auf die Regeln, die zur Java-Leistungsoptimierung befolgt werden können.

Dieser Artikel konzentriert sich auf die Theorie, auf die Praxis. In den folgenden Artikeln werden weitere Fälle verwendet, um die Wissenspunkte dieses Artikels zu verfeinern, der zum wiederholten Nachdenken und zur Einführung geeignet ist.

Übersicht Leistungsoptimierung wird entsprechend der Optimierungskategorie in Geschäftsoptimierung und technische Optimierung unterteilt. Der Effekt der Geschäftsoptimierung ist ebenfalls sehr groß, gehört aber zur Kategorie Produkte und Management. Als Programmierer sind wir in unserer täglichen Arbeit mit Optimierungsmethoden konfrontiert, die hauptsächlich auf einer Reihe technischer Mittel basieren, um die festgelegten Optimierungsziele zu erreichen. Ich kann diese Reihe technischer Mittel grob in die folgenden sieben Kategorien zusammenfassen:

Interview: Wissen Sie, welche Methoden zur Java-Performance-Optimierung verfügbar sind?Wie Sie sehen, konzentriert sich die Optimierungsmethode auf die Planung von Rechenressourcen und Speicherressourcen. In Optimierungsmethoden gibt es viele Möglichkeiten, Raum gegen Zeit auszutauschen. Es ist jedoch nicht ratsam, nur die Berechnungsgeschwindigkeit zu berücksichtigen, ohne Komplexität und Platzprobleme zu berücksichtigen. Was wir tun müssen, ist, eine optimale Ressourcennutzung zu erreichen und gleichzeitig auf die Leistung zu achten.

Als nächstes werde ich diese 7 Optimierungsrichtungen kurz vorstellen. Wenn Sie sich langweilig fühlen, ist das in Ordnung. Der Zweck dieses Artikels besteht darin, Ihnen einen Überblick über die Gesamtpunktzahl und ein allgemeines Verständnis der theoretischen Grundlagen zu geben.

Wiederverwendungsoptimierung Beim Schreiben von Code werden Sie feststellen, dass es viele wiederholte Codes gibt, die extrahiert und in öffentliche Methoden umgewandelt werden können. Auf diese Weise müssen Sie es bei der nächsten Verwendung nicht erneut schreiben.

Diese Idee ist Wiederverwendung. Die obige Beschreibung ist eine Optimierung der Codierungslogik. Für den Datenzugriff besteht die gleiche Wiederverwendungssituation. Ob im Leben oder beim Programmieren: Ohne Wiederverwendung werden Arbeit und Leben ermüdender.

Wenn es in Softwaresystemen um die Wiederverwendung von Daten geht, denken wir zuerst an Pufferung und Caching. Beachten Sie den Unterschied zwischen diesen beiden Wörtern. Viele Schüler sind leicht zu verwechseln.

Puffer wird häufig verwendet, um Daten vorübergehend zu speichern und sie dann stapelweise zu übertragen oder zu schreiben. Die sequentielle Methode wird hauptsächlich verwendet, um häufige und langsame zufällige Schreibvorgänge zwischen verschiedenen Geräten zu erleichtern. Die Pufferung zielt hauptsächlich auf Schreibvorgänge ab.

Cache (Cache) wird häufig zur Wiederverwendung gelesener Daten verwendet. Durch die Zwischenspeicherung in einem relativ schnellen Bereich ist das Caching hauptsächlich auf Lesevorgänge ausgerichtet.

In ähnlicher Weise wird in Java sehr häufig der Pooling-Vorgang von Objekten wie Datenbankverbindungspools, Thread-Pools usw. verwendet. Da die Kosten für die Erstellung und Zerstörung dieser Objekte relativ hoch sind, werden wir diese Objekte nach der Verwendung auch vorübergehend speichern. Wenn wir sie das nächste Mal verwenden, müssen wir den zeitaufwändigen Initialisierungsvorgang nicht erneut durchführen.

Computing-Optimierung

Parallele Ausführung

Heutzutage entwickeln sich CPUs sehr schnell und die meiste Hardware ist Multi-Core. Wenn Sie die Ausführung einer bestimmten Aufgabe beschleunigen möchten, besteht die schnellste und beste Lösung darin, sie parallel ausführen zu lassen. Es gibt drei Modi der parallelen Ausführung:

Der erste Modus ist Multi-Machine, der den Lastausgleich nutzt, um Datenverkehr oder große Berechnungen in mehrere Teile aufzuteilen und diese gleichzeitig zu verarbeiten. Hadoop verwendet beispielsweise MapReduce, um Aufgaben aufzuteilen und mehreren Maschinen die gleichzeitige Durchführung von Berechnungen zu ermöglichen.

Der zweite Modus ist die Verwendung von Multiprozessen. Beispielsweise verwendet Nginx das NIO-Programmiermodell. Der Master verwaltet den Worker-Prozess auf einheitliche Weise, und dann führt der Worker-Prozess das eigentliche Anforderungs-Proxying durch. Dadurch können auch mehrere CPUs der Hardware gut genutzt werden.

Der dritte Modus ist die Verwendung von Multithreading, dem Java-Programmierer am meisten ausgesetzt sind. Netty nutzt beispielsweise das Reactor-Programmiermodell und nutzt auch NIO, allerdings ist es Thread-basiert. Der Boss-Thread wird verwendet, um Anfragen zu empfangen und sie dann für echte Geschäftsberechnungen an die entsprechenden Worker-Threads zu verteilen.

Sprachen wie Golang haben eine leichtere Existenz als Threads, aber sie sind in Java noch nicht ausgereift, daher werde ich sie nicht allzu oft vorstellen, aber im Wesentlichen ist sie auch für Multi geeignet -Kernanwendungen, die die parallele Ausführung von Aufgaben ermöglichen.

Wechsel von synchron zu asynchron

Eine weitere Optimierung für die Datenverarbeitung ist der Wechsel von synchron zu asynchron, was normalerweise Änderungen im Programmiermodell mit sich bringt. Im synchronen Modus wird die Anfrage blockiert, bis ein Erfolgs- oder Fehlerergebnis zurückgegeben wird. Obwohl das Programmiermodell einfach ist, ist es besonders problematisch, wenn es um plötzlichen und unregelmäßigen Datenverkehr in bestimmten Zeiträumen geht, und Anfragen können leicht fehlschlagen.

Asynchrone Vorgänge können die horizontale Expansion problemlos unterstützen, den sofortigen Druck verringern und Anforderungen reibungsloser gestalten. Synchrone Anfragen sind wie Fäuste, die auf Stahlplatten schlagen; asynchrone Anfragen sind wie Fäuste, die auf Schwämme schlagen. Sie können sich diesen Prozess vorstellen, letzterer ist auf jeden Fall flexibler und die Erfahrung ist freundlicher.

Lazy Loading

Die letzte Möglichkeit besteht darin, einige gängige Entwurfsmuster zu verwenden, um das Geschäft zu optimieren und das Erlebnis zu verbessern, z. B. den Singleton-Modus, den Proxy-Modus usw. Wenn Sie beispielsweise beim Zeichnen eines Swing-Fensters mehr Bilder anzeigen möchten, können Sie zuerst einen Platzhalter laden und dann die erforderlichen Ressourcen langsam über einen Hintergrundthread laden, wodurch verhindert werden kann, dass das Fenster einfriert.

Ergebnismengenoptimierung

Als nächstes stellen wir die Optimierung der Ergebnismenge vor. Um ein intuitiveres Beispiel zu geben: Wir alle wissen, dass die Darstellungsform von XML sehr gut ist. Warum gibt es also immer noch JSON? Ein wichtiger Grund ist nicht nur, dass es einfacher zu schreiben ist, sondern auch, dass seine Größe kleiner geworden ist und seine Übertragungseffizienz und Analyseeffizienz höher geworden ist. Ebenso wie bei Googles Protobuf ist seine Größe noch kleiner. Obwohl die Lesbarkeit verringert ist, kann in einigen Szenarien mit hoher Parallelität (z. B. RPC) die Effizienz erheblich verbessert werden, was eine typische Optimierung von Ergebnismengen darstellt.

Das liegt daran, dass sich unsere aktuellen Webdienste alle im C/S-Modus befinden. Wenn Daten vom Server zum Client übertragen werden, müssen mehrere Kopien verteilt werden. Die Datenmenge wächst schnell. Jedes Mal, wenn eine kleine Speichermenge reduziert wird, steigen die Übertragungsleistung und die Kosten erheblich.

Wie bei Nginx ist die GZIP-Komprimierung normalerweise aktiviert, um den übertragenen Inhalt kompakt zu halten. Der Client benötigt nur wenig Rechenleistung, um die Dekomprimierung zu ermöglichen. Da dieser Vorgang dezentral erfolgt, ist die Leistungseinbuße festgelegt.

Wenn wir dieses Prinzip verstehen, können wir die allgemeine Idee der Optimierung von Ergebnismengen erkennen. Sie sollten versuchen, die zurückgegebenen Daten so einfach wie möglich zu halten. Wenn einige Felder vom Client nicht benötigt werden, entfernen Sie sie im Code oder direkt in der SQL-Abfrage.

Für einige Unternehmen, die keine hohen Anforderungen an die Pünktlichkeit, aber hohe Anforderungen an die Verarbeitungsfähigkeiten haben. Wir müssen aus den Erfahrungen mit Puffern lernen, Netzwerkverbindungsinteraktionen minimieren und Stapelverarbeitung verwenden, um die Verarbeitungsgeschwindigkeit zu erhöhen.

Der Ergebnissatz wird wahrscheinlich zweimal verwendet und Sie können ihn zum Cache hinzufügen, aber es mangelt ihm immer noch an Geschwindigkeit. Zu diesem Zeitpunkt ist es notwendig, die Verarbeitung der Datenerfassung zu optimieren, indem Indizes oder Bitmap-Bitmaps verwendet werden, um den Datenzugriff zu beschleunigen.

Ressourcenkonfliktoptimierung

In unserer normalen Entwicklung werden wir viele gemeinsame Ressourcen einbeziehen. Einige dieser gemeinsam genutzten Ressourcen sind eigenständige Ressourcen, beispielsweise eine HashMap. Andere sind einzelne Ressourcen, beispielsweise Setnx eines bestimmten Redis-Schlüssels , verteilte Transaktionen usw.

In Wirklichkeit gibt es viele Leistungsprobleme im Zusammenhang mit Sperren. Die meisten von uns denken an Datenbankzeilensperren, Tabellensperren, verschiedene Sperren in Java usw. Auf der unteren Ebene, wie z. B. Sperren auf CPU-Befehlsebene, Sperren auf JVM-Befehlsebene, interne Sperren des Betriebssystems usw., kann man sagen, dass sie überall sind.

Nur Parallelität kann zu Ressourcenkonflikten führen. Das heißt, gleichzeitig kann nur eine Verarbeitungsanforderung die gemeinsam genutzten Ressourcen erhalten. Die Möglichkeit, Ressourcenkonflikte zu lösen, besteht darin, sie zu sperren. Ein weiteres Beispiel ist eine Transaktion, bei der es sich im Wesentlichen um eine Art Sperre handelt.

Je nach Sperrstufe können Sperren in optimistische Sperren und pessimistische Sperren unterteilt werden. Je nach Sperrtyp gibt es Sperren, die in faire und unfaire Sperren unterteilt sind subtile Unterschiede.

Inhalte für Ressourcen führen zu ernsthaften Leistungsproblemen. Daher werden einige Untersuchungen zu sperrfreien Warteschlangen durchgeführt, und die Leistungsverbesserung wird enorm sein.

Algorithmenoptimierung

Algorithmen können die Leistung komplexer Unternehmen erheblich verbessern, in tatsächlichen Unternehmen handelt es sich jedoch häufig um Variationen. Da Speicher immer billiger wird, wird in einigen Unternehmen, in denen die CPU sehr knapp ist, häufig Platz gegen Zeit eingetauscht, um die Verarbeitung zu beschleunigen.

Der Algorithmus gehört zum Code-Tuning. Code-Tuning erfordert viele Programmierkenntnisse und erfordert, dass Benutzer mit der API der verwendeten Sprache sehr vertraut sind. Manchmal ist auch der flexible Einsatz von Algorithmen und Datenstrukturen ein wichtiger Bestandteil der Codeoptimierung. Zu den häufig verwendeten Methoden zur Reduzierung der Zeitkomplexität gehören beispielsweise Rekursion, Halbierung, Sortierung, dynamische Programmierung usw.

Eine hervorragende Implementierung hat größere Auswirkungen auf das System als eine schlechte Implementierung. Als List-Implementierungen unterscheiden sich LinkedList und ArrayList beispielsweise um mehrere Größenordnungen in der Direktzugriffsleistung. In einem anderen Beispiel verwendet CopyOnWriteList eine Copy-on-Write-Methode, die Sperrkonflikte in Szenarien mit mehr und weniger Lesevorgängen erheblich reduzieren kann Schreiben. Wann wir Synchronisation verwenden und wann wir Thread-sicher sein müssen, stellt auch höhere Anforderungen an unsere Codierungsfunktionen.

Dieser Teil des Wissens erfordert, dass wir darauf achten, es in unserer täglichen Arbeit anzusammeln. In den folgenden Kursen werden weitere wichtige Wissenspunkte ausgewählt und erläutert.

Effiziente Implementierung

Versuchen Sie bei der täglichen Programmierung, einige Komponenten mit guten Designkonzepten und überlegener Leistung zu verwenden. Mit Netty müssen Sie sich beispielsweise nicht mehr für die älteren Mina-Komponenten entscheiden. Wählen Sie beim Entwerfen eines Systems unter Berücksichtigung von Leistungsfaktoren kein zeitaufwändiges Protokoll wie SOAP. Ein weiteres Beispiel: Ein guter Syntaxanalysator (z. B. mit JavaCC) ist viel effizienter als reguläre Ausdrücke.

Kurz gesagt: Wenn der Engpass des Systems durch Testanalysen gefunden wird, müssen die Schlüsselkomponenten durch effizientere Komponenten ersetzt werden. In diesem Fall ist das Adaptermuster sehr wichtig. Aus diesem Grund fügen viele Unternehmen gerne eine Abstraktionsebene über vorhandenen Komponenten hinzu, und wenn die zugrunde liegenden Komponenten ausgetauscht werden, bemerken die Anwendungen der oberen Ebene dies nicht.

JVM-Optimierung

Da Java auf der virtuellen JVM-Maschine ausgeführt wird, werden viele seiner Funktionen durch die JVM eingeschränkt. Durch die Optimierung der virtuellen JVM-Maschine kann bis zu einem gewissen Grad auch die Leistung von JAVA-Programmen verbessert werden. Wenn Parameter falsch konfiguriert sind, kann dies sogar schwerwiegende Folgen wie OOM haben.

Der derzeit am weitesten verbreitete Garbage Collector ist G1. Mit sehr wenigen Parameterkonfigurationen kann Speicher effizient recycelt werden. Der CMS-Garbage Collector wurde in Java 14 entfernt. Da seine GC-Zeit nicht kontrollierbar ist, sollte seine Verwendung nach Möglichkeit vermieden werden.

JVM-Leistungsoptimierung erfordert Kompromisse in allen Aspekten, die sich häufig auf den gesamten Körper auswirken, und die Auswirkungen aller Aspekte müssen umfassend berücksichtigt werden. Daher ist es besonders wichtig, einige der Funktionsprinzipien innerhalb der JVM zu verstehen. Dies wird uns helfen, ein tieferes Verständnis des Codes zu erlangen und effizienteren Code zu schreiben.

Zusammenfassung

Die oben genannten sind die 7 allgemeinen Richtungen der Codeoptimierung. Durch eine kurze Einführung vermitteln wir jedem ein allgemeines Verständnis des Inhalts der Leistungsoptimierung. Diese sieben Hauptrichtungen sind die wichtigsten Richtungen der Codeoptimierung. Natürlich umfassen die Leistungsoptimierung auch Datenbankoptimierung, Betriebssystemoptimierung und andere Inhalte. In den folgenden Artikeln werden wir sie nicht kurz vorstellen Zusammenfassung vorstellen.

Das obige ist der detaillierte Inhalt vonInterview: Wissen Sie, welche Methoden zur Java-Performance-Optimierung verfügbar sind?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:Java后端技术全栈. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen