Dieser Artikel ist ein Auszug aus „Java-Leistung“. Studenten, die sich mehr Gedanken über die Java-Leistung machen, kennen dieses Buch wahrscheinlich. Leistung ist vielleicht etwas, das vielen Studenten beim täglichen Schreiben von Java-Code selten wichtig ist, aber in unserem Fall Beim Schreiben von Code sind die Auswirkungen auf die Programmleistung immer untrennbar miteinander verbunden. Von der Verwendung von Bitoperationen zur Implementierung arithmetischer Operationen bis hin zum gesamten Architekturdesign von JAVA-Code liegt uns die Leistung tatsächlich sehr am Herzen. In diesem Artikel werden hauptsächlich einige Punkte erwähnt, hauptsächlich einige Themen, die uns im Bereich der Leistung am meisten beschäftigen, und er ist aufschlussreich. Wenn Schüler an Leistung interessiert sind, können wir jeden Punkt gemeinsam eingehend untersuchen.
Für die Leistungsoptimierung gibt es normalerweise drei Schritte: 1. Leistungsüberwachung 2. Leistungsanalyse 3. Leistungsoptimierung
Unsere Hauptanliegen hinsichtlich der Leistung des Betriebssystems sind folgende: Punkte: CPU-Auslastung, CPU-Scheduling-Ausführungswarteschlange, Speicherauslastung, Netzwerk-E/A, Festplatten-E/A.
1.CPU-Auslastung
Damit die Anwendung die beste Leistung und Skalierbarkeit erreicht, müssen wir nicht nur den verfügbaren Teil des CPU-Zyklus vollständig nutzen, sondern auch Machen Sie außerdem die Nutzung dieses Teils der CPU wertvoller, anstatt ihn zu verschwenden. Die volle Ausnutzung der CPU-Zyklen ist für Multithread-Anwendungen, die auf Multiprozessor- und Multicore-Systemen laufen, eine große Herausforderung. Wenn die CPU die Sättigung erreicht, bedeutet dies außerdem nicht, dass die Leistung und Skalierbarkeit der CPU den optimalen Zustand erreicht haben. Um zu unterscheiden, wie Anwendungen CPU-Ressourcen nutzen, müssen wir dies auf Betriebssystemebene erkennen. Auf vielen Betriebssystemen umfassen CPU-Auslastungsstatistikberichte normalerweise die Benutzer- und System- oder Kernel-Nutzung des Betriebssystems. Die Benutzernutzung der CPU ist die Zeit, die eine Anwendung für die Ausführung des Anwendungscodes benötigt. Im Gegensatz dazu bezieht sich die Kernel- und System-CPU-Auslastung auf die Zeit, die eine Anwendung mit der Ausführung von Betriebssystem-Kernel-Codesperren verbringt. Eine hohe Kernel- oder System-CPU-Auslastung kann auf einen Engpass bei gemeinsam genutzten Ressourcen oder eine große Interaktion mit E/A-Geräten hinweisen. Um die Anwendungsleistung und Skalierbarkeit zu verbessern, besteht der ideale Zustand darin, die Kernel- oder System-CPU-Zeit auf 0 % zu beschränken, da die Zeit, die für die Ausführung von Kernel- oder Systemcode aufgewendet wird, für die Ausführung von Anwendungscode verwendet werden kann. Daher besteht die richtige Richtung zur Optimierung der CPU-Nutzung darin, die Zeit, die die CPU mit der Ausführung von Kernel-Code oder Systemcode verbringt, so weit wie möglich zu reduzieren.
Bei rechenintensiven Anwendungen geht die Leistungsüberwachung tiefer als die Überwachung der CPU-Auslastung durch den Benutzer und der Kernel- oder System-CPU. Bei rechenintensiven Anwendungen müssen wir die Anzahl der Ausführungen innerhalb eines CPU-Taktzyklus überwachen (Anweisungen pro Takt; IPC) oder die von jeder CPU-Ausführung verwendeten CPU-Zyklen (Zyklen pro Befehl; CPI). Für rechenintensive Anwendungen ist es eine gute Wahl, die CPU aus diesen beiden Dimensionen zu überwachen, da die paketierten CPU-Leistungsberichtstools moderner Betriebssysteme normalerweise nur die CPU-Auslastung ausdrucken, nicht jedoch die CPU-Auslastung während des CPU-Zyklus um die Anweisung auszuführen. Dies bedeutet, dass das CPU-Leistungsberichtstool des Betriebssystems die CPU auch als „Stall“ betrachtet, wenn sie auf Daten im Speicher wartet Die CPU Jedes Mal, wenn eine Anweisung ausgeführt wird, tritt ein „Stall“-Szenario auf, solange die für die Anweisung erforderlichen Daten nicht bereit sind, sich also nicht im Register oder im CPU-Cache befinden.
Wenn das „Stall“-Szenario auftritt, verschwendet die CPU Taktzyklen, da die CPU darauf warten muss, dass die von der Anweisung benötigten Daten im Register oder Puffer ankommen. Und in diesem Szenario ist es normal, dass Hunderte von CPU-Taktzyklen verschwendet werden. Daher besteht die Strategie zur Verbesserung der Leistung darin, das Auftreten von „Stall“-Szenarien zu reduzieren oder die Nutzung des CPU-Cache zu verbessern Es werden weniger CPU-Zyklen mit dem Warten auf Daten verschwendet. Diese Art von Wissen über die Leistungsüberwachung geht über den Inhalt dieses Buches hinaus und erfordert die Hilfe eines Leistungsexperten. Das später erwähnte Leistungsanalysetool Oracle Solaris Studio Performance Analyzer wird solche Daten jedoch enthalten.
2.CPU-Planungswarteschlange
Zusätzlich zur Überwachung der CPU-Auslastung können wir auch überprüfen, ob das System vollständig ausgelastet ist, indem wir die CPU-Ausführungswarteschlange überwachen. Die Ausführungswarteschlange wird zum Speichern von Lightweight-Prozessen verwendet. Diese Prozesse sind normalerweise zur Ausführung bereit, warten jedoch auf die CPU-Planung und befinden sich in der Planungswarteschlange, wenn die Anzahl der Lightweight-Prozesse zunimmt, die der aktuelle Prozessor verarbeiten kann Da es viele gibt, werden Planungswarteschlangen generiert. Eine tiefe CPU-Dispatch-Warteschlange zeigt an, dass das System vollständig ausgelastet ist. Die Tiefe der Ausführungswarteschlange des Systems entspricht der Anzahl der Wartezeiten, die vom virtuellen Prozessor nicht ausgeführt werden können, und die Anzahl der virtuellen Prozessoren entspricht der Anzahl der Hardware-Threads im System. Wir können die Java-API verwenden, um die Anzahl der virtuellen Prozessoren zu ermitteln: Runtime.avaliableProcessors(). Wenn die Tiefe der Ausführungswarteschlange viermal oder mehr als die Anzahl der virtuellen Prozessoren beträgt, reagiert das Betriebssystem nicht mehr.
Eine allgemeine Richtlinie zum Erkennen von CPU-Scheduling-Warteschlangen besteht darin, aufmerksam zu sein, wenn wir feststellen, dass die Warteschlangentiefe mehr als das Doppelte der Anzahl virtueller Prozesse beträgt, aber kein Bedarf besteht, sofort Maßnahmen zu ergreifen. Wenn es mehr als das Dreifache oder das Vierfache oder mehr beträgt, sollten Sie aufmerksam sein und das Problem unverzüglich lösen.
Es gibt normalerweise zwei optionale Möglichkeiten, die Tiefe der Warteschlange zu beobachten. Die erste besteht darin, die Last durch Hinzufügen von CPUs zu teilen oder die Last auf vorhandenen CPUs zu reduzieren. Dieser Ansatz reduziert im Wesentlichen die Anzahl der Ladethreads pro Ausführungseinheit und verringert dadurch die Tiefe der Ausführungswarteschlange.
Eine andere Möglichkeit besteht darin, die CPU-Auslastung zu erhöhen, indem die auf dem System ausgeführten Anwendungen profiliert werden. Mit anderen Worten: Finden Sie eine Möglichkeit, die für die Speicherbereinigung aufgewendeten CPU-Zyklen zu reduzieren, oder finden Sie einen besseren Algorithmus, um weniger CPU-Zyklen auszuführen CPU-Anweisungen. Leistungsexperten konzentrieren sich normalerweise auf den letztgenannten Ansatz: Reduzierung der Codeausführungspfadlänge und bessere CPU-Befehlsauswahl. JAVA-Programmierer können die Effizienz der Codeausführung durch bessere Ausführungsalgorithmen und Datenstrukturen verbessern.
3. Speicherauslastung
Zusätzlich zur CPU-Auslastung müssen auch die Speichereigenschaften des Systems überwacht werden, zum Beispiel: Paging, Swapping, Sperren, Kontextwechsel durch Multithreading usw. .
Swapping findet normalerweise statt, wenn der von der Anwendung benötigte Speicher größer ist als der tatsächliche physische Speicher. Um mit dieser Situation umzugehen, konfiguriert das Betriebssystem normalerweise einen entsprechenden Bereich, den sogenannten Swap-Bereich. Der Auslagerungsbereich befindet sich normalerweise auf der physischen Festplatte. Wenn die Anwendung im physischen Speicher erschöpft ist, lagert das Betriebssystem vorübergehend einen Teil der Speicherdaten auf dem Festplattenspeicher aus Zugriffshäufigkeit, ohne den Vergleich zu beeinträchtigen. Wenn die Anwendung auf den ausgelagerten Speicher zugreift, muss der Speicher in Seiteneinheiten ausgelesen werden der Bewerbung.
Die Leistung des Garbage Collectors der virtuellen Maschine ist während des Austauschs sehr schlecht, da die meisten vom Garbage Collector besuchten Bereiche nicht erreichbar sind, d. h. der Garbage Collector verursacht Austauschaktivitäten. Die Szene ist dramatisch. Wenn der Garbage-Collector-Heap-Bereich auf Festplattenspeicher verlagert wurde, erfolgt die Verlagerung zu diesem Zeitpunkt in Seiteneinheiten, sodass er vom Garbage Collector gescannt werden kann. Die Sammelzeit des Kollektors wird zu diesem Zeitpunkt verlängert, wenn der Garbage Collector „Stop The World“ ist (wodurch die Anwendungsantwort gestoppt wird).
4. Netzwerk-E/A
Die Leistung und Skalierbarkeit verteilter JAVA-Anwendungen wird durch die Netzwerkbandbreite und die Netzwerkleistung begrenzt. Wenn wir beispielsweise mehr Pakete an eine Netzwerkschnittstelle senden, als diese verarbeiten kann, sammeln sich Pakete im Puffer des Betriebssystems an, was zu Anwendungsverzögerungen führt, und in anderen Situationen kommt es auch zu Verzögerungen bei Netzwerkanwendungen.
Tools zur Differenzierung und Überwachung sind in Betriebssystem-Paketierungstools oft schwer zu finden. Obwohl Linux den Befehl „netstat“ bereitstellt, bieten sowohl Linux als auch Solaris eine Implementierung der Netzwerknutzung. Beide liefern Statistiken, einschließlich Paketversand, Paketempfang, Fehlerpakete, Konflikte und andere Informationen pro Sekunde. Bei Ethernet ist es normal, dass eine kleine Anzahl von Paketkollisionen auftritt. Treten viele Paketfehler auf, liegt möglicherweise ein Problem mit der Netzwerkkarte vor. Obwohl Netstat die Sende- und Empfangsdaten der Netzwerkschnittstelle zählen kann, ist es gleichzeitig schwierig festzustellen, ob die Netzwerkkarte vollständig ausgelastet ist. Wenn netstat -i beispielsweise anzeigt, dass 2500 Pakete pro Sekunde von der Netzwerkkarte gesendet werden, wir aber immer noch nicht feststellen können, ob die aktuelle Netzwerkauslastung 100 % oder 1 % beträgt, können wir nur wissen, dass derzeit Datenverkehr vorhanden ist. Dies ist nur eine Schlussfolgerung, die ohne Kenntnis der Netzwerkpaketgröße gezogen werden kann. Einfach ausgedrückt können wir den von Linux und Solaris bereitgestellten Netstat nicht verwenden, um festzustellen, ob das aktuelle Netzwerk die Leistung beeinflusst. Wir benötigen einige andere Tools, um das Netzwerk zu überwachen, während unsere JAVA-Anwendung ausgeführt wird.
5. Festplatten-E/A
Wenn die Anwendung auf der Festplatte ausgeführt wird, müssen wir die Festplatte überwachen, um mögliche Probleme mit der Festplattenleistung zu überwachen. Einige Anwendungen sind E/A-intensiv, beispielsweise Datenbanken. Der Einsatz von Datenträgern erfolgt in der Regel auch in Anwendungsprotokollsystemen. Protokolle dienen in der Regel dazu, wichtige Informationen während des Systembetriebs aufzuzeichnen.