Ist eine LinkedList schneller? Soll ich `for each` durch einen `iterator` ersetzen? Soll diese `ArrayList` ein `Array` sein? Dieser Artikel entstand als Reaktion auf eine Optimierung, die so bösartig war, dass sie sich dauerhaft in mein Gedächtnis eingebrannt hat.
Bevor wir uns direkt mit Java und den Möglichkeiten zur Beseitigung von Störungen befassen, sei es durch den Garbage Collector oder durch Kontextwechsel, wollen wir zunächst einen Blick auf die Grundlagen des Codeschreibens für Ihr zukünftiges Ich werfen.
Vorzeitige Optimierung ist die Wurzel allen Übels.
Sie haben es schon einmal gehört: Vorzeitige Optimierung ist die Wurzel allen Übels. Naja, manchmal zumindest. Beim Schreiben von Software bin ich fest davon überzeugt, dass man:
so beschreibend wie möglich ; Sie sollten versuchen, Absichten so zu erzählen, als würden Sie eine Geschichte schreiben.
so optimal wie möglich ; das heißt, Sie sollten die Grundlagen der Sprache kennen und entsprechend anwenden.
Ihr Code sollte Ihre Absicht zum Ausdruck bringen und dies hängt maßgeblich von der Art und Weise ab, wie Sie Methoden und Variablen benennen.
int[10] array1; // bad int[10] numItems; // better int[10] backPackItems; // great
Alleine anhand des Variablennamens lässt sich bereits auf die Funktionalität schließen.
Während numItems
abstrakt ist, sagt backPackItems
viel über das erwartete Verhalten aus.
Oder sagen wir, Sie haben diese Methode:
List<Countries> visitedCountries() { if(noCountryVisitedYet) return new ArrayList<>(0); } // (...) return listOfVisitedCountries; }
Was den Code angeht, sieht das mehr oder weniger in Ordnung aus.
Können wir das besser machen? Das können wir auf jeden Fall!
List<Countries> visitedCountries() { if(noCountryVisitedYet) return Collections.emptyList(); } // (...) return listOfVisitedCountries; }
Das Lesen von Collections.emptyList()
ist viel aussagekräftiger als new ArrayList<>(0);
Stellen Sie sich vor, Sie lesen den obigen Code zum ersten Mal und stoßen auf die Schutzklausel , die überprüft, ob der Benutzer tatsächlich Länder besucht hat. Stellen Sie sich außerdem vor, dass dies in einer langen Klasse vergraben ist. Das Lesen von Collections.emptyList()
ist definitiv aussagekräftiger als new ArrayList<>(0)
. Sie stellen außerdem sicher, dass es unveränderlich ist und dass Clientcode es nicht ändern kann.
Kennen Sie Ihre Sprache und verwenden Sie sie entsprechend. Wenn Sie ein double
benötigen, müssen Sie es nicht in ein Double
Objekt einschließen. Dasselbe gilt für die Verwendung einer List
, wenn Sie eigentlich nur ein Array
benötigen.
Beachten Sie, dass Sie Zeichenfolgen mit StringBuilder
oder StringBuffer
verketten sollten, wenn Sie den Status zwischen Threads teilen:
// don't do this String votesByCounty = ""; for (County county : counties) { votesByCounty += county.toString(); } // do this instead StringBuilder votesByCounty = new StringBuilder(); for (County county : counties) { votesByCounty.append(county.toString()); }
Erfahren Sie, wie Sie Ihre Datenbank indizieren. Planen Sie Engpässe voraus und führen Sie die Zwischenspeicherung entsprechend durch. Alle oben genannten Punkte sind Optimierungen. Sie sollten sich dieser Art von Optimierungen bewusst sein und sie als Erste umsetzen.
Ich werde nie einen Schreiberling vergessen, den ich vor ein paar Jahren gelesen habe. Ehrlich gesagt, der Autor ruderte schnell zurück, aber es zeigt, wie viel Böses aus guten Absichten entstehen kann.
// do not do this, ever! int i = 0; while (i<10000000) { // business logic if (i % 3000 == 0) { //prevent long gc try { Thread.sleep(0); } catch (Ignored e) { } } }
Ein Garbage Collector-Hack aus der Hölle!
Im Originalartikel können Sie mehr darüber lesen, warum und wie der obige Code funktioniert. Auch wenn der Exploit auf jeden Fall interessant ist, handelt es sich hierbei um eines der Dinge, die Sie niemals tun sollten.
Thread.sleep(0)
hat in diesem Block keinen Zweck
Beginnen Sie erst dann mit der Entwicklung komplexerer Texte, wenn Sie nach dem Schreiben mit allen von der Sprache bereitgestellten Standardoptimierungen auf einen Engpass gestoßen sind. Vermeiden Sie jedoch Mixturen wie die oben beschriebenen.
Wenn der Garbage Collector nach all dem immer noch Widerstand leistet, können Sie Folgendes versuchen:
Wenn Ihr Dienst so latenzempfindlich ist, dass Sie keine GC zulassen können, führen Sie ihn mit „Epsilon GC“ aus und vermeiden Sie GC vollständig .
-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
Dadurch wird Ihr Speicher natürlich vergrößert, bis Sie eine OOM-Ausnahme erhalten. Entweder handelt es sich also um ein kurzlebiges Szenario oder Ihr Programm ist so optimiert, dass keine Objekte erstellt werden
Wenn Ihr Dienst etwas latenzempfindlich ist, die zulässige Toleranz aber einen gewissen Spielraum lässt , führen Sie GC1 aus und geben Sie etwas wie -XX:MaxGCPauseTimeMillis=100
ein (Standard ist 250 ms).
Wenn das Problem von externen Bibliotheken herrührt , beispielsweise wenn eine davon System.gc()
oder Runtime.getRuntime().gc()
aufruft, bei denen es sich um Garbage Collector handelt, die die Welt stoppen, können Sie das fehlerhafte Verhalten durch Ausführen mit -XX:+DisableExplicitGC
überschreiben.
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
. Vielleicht möchten Sie sich auch diesen JDK 21 GC-Benchmark ansehen.
VERSION START | VERSION ENDE | STANDARD-GC |
---|---|---|
Java 1 | Java 4 | Serieller Garbage Collector |
Java 5 | Java 8 | Paralleler Garbage Collector |
Java 9 | laufend | G1 Müllsammler |
Hinweis 1: Seit Java 15 ist ZGC
produktionsbereit , Sie müssen es jedoch noch explizit mit -XX:+UseZGC
aktivieren.
Hinweis 2: Die VM betrachtet Maschinen als Server-Klasse, wenn die VM mehr als zwei Prozessoren und eine Heap-Größe größer oder gleich 1792 MB erkennt. Wenn es sich nicht um eine Server-Klasse handelt, wird standardmäßig der Serial GC verwendet .
Entscheiden Sie sich im Wesentlichen für GC-Tuning, wenn klar ist, dass die Leistungseinschränkungen der Anwendung direkt mit dem Garbage Collection-Verhalten zusammenhängen und Sie über das notwendige Fachwissen verfügen, um fundierte Anpassungen vorzunehmen. Andernfalls vertrauen Sie den Standardeinstellungen der JVM und konzentrieren Sie sich auf die Optimierung des Codes auf Anwendungsebene.
u/shiphe - du solltest den ganzen Kommentar lesen
Wenn Sie ohne echte Benchmarks aus dem Gefühl heraus optimieren , tun Sie sich selbst keinen Gefallen. JMH ist die De-facto -Java-Bibliothek zum Testen der Leistung Ihrer Algorithmen. Verwenden Sie sie.
Das Fixieren eines Prozesses auf einen bestimmten Kern kann die Anzahl der Cache-Treffer verbessern. Dies hängt von der zugrunde liegenden Hardware und davon ab, wie Ihre Routine mit Daten umgeht. Diese Bibliothek macht die Implementierung jedoch so einfach, dass Sie eine CPU-intensive Methode testen sollten, wenn Sie Probleme mit ihr haben.
Dies ist eine dieser Bibliotheken, die Sie studieren möchten, auch wenn Sie sie nicht brauchen. Die Idee besteht darin, Parallelität mit ultraniedriger Latenz zu ermöglichen. Aber die Art und Weise, wie sie implementiert wird, von mechanischer Sympathie bis zum Ringpuffer, bringt viele neue Konzepte mit sich. Ich erinnere mich noch daran, wie ich sie vor sieben Jahren zum ersten Mal entdeckte und eine ganze Nacht durchmachte, um sie zu verarbeiten.
Die Prämisse von jvmquake
ist, dass Sie möchten, dass die JVM abstürzt und nicht hängen bleibt, wenn etwas schief geht. Vor ein paar Jahren habe ich Simulationen auf einem HTCondor-Cluster ausgeführt, der enge Speicherbeschränkungen hatte, und manchmal blieben Jobs aufgrund von „Nicht genügend Arbeitsspeicher“-Fehlern hängen.
Diese Bibliothek erzwingt einen Abbruch der JVM, sodass Sie sich mit dem eigentlichen Fehler befassen können. In diesem speziellen Fall würde HTCondor den Job automatisch neu planen.
Der Code, der mich dazu gebracht hat, diesen Beitrag zu schreiben? Ich habe schon viel Schlimmeres geschrieben. Und das tue ich immer noch. Das Beste, worauf wir hoffen können, ist, dass wir kontinuierlich weniger Fehler machen.
Ich rechne damit, dass ich in einigen Jahren unzufrieden sein werde, wenn ich mir meinen eigenen Code anschaue.
Und das ist ein gutes Zeichen.
visitedCountries()
und die ausführliche Erklärung.Auch veröffentlicht auf wasteofserver.com