Nun, hier gibt es mehrere Fragen in einer!
1 - Wie werden kurzlebige Objekte verwaltet?
Wie bereits erwähnt, kann die JVM perfekt mit einer großen Menge kurzlebiger Objekte umgehen, da sie der schwachen Generationshypothese folgt .
Beachten Sie, dass es sich um Objekte handelt, die den Hauptspeicher (Heap) erreicht haben. Dies ist nicht immer der Fall. Viele von Ihnen erstellte Objekte hinterlassen nicht einmal ein CPU-Register. Betrachten Sie zum Beispiel diese for-Schleife
for(int i=0, i<max, i++) {
// stuff that implies i
}
Denken wir nicht an das Abrollen von Schleifen (eine Optimierung, die die JVM stark an Ihrem Code ausführt). Wenn max
gleich ist Integer.MAX_VALUE
, kann die Ausführung Ihrer Schleife einige Zeit dauern. Die i
Variable wird jedoch niemals aus dem Schleifenblock entkommen. Daher legt die JVM diese Variable in einem CPU-Register ab, erhöht sie regelmäßig, sendet sie jedoch niemals an den Hauptspeicher zurück.
Das Erstellen von Millionen von Objekten ist also keine große Sache, wenn sie nur lokal verwendet werden. Sie werden tot sein, bevor sie in Eden gelagert werden, sodass der GC sie nicht einmal bemerkt.
2 - Ist es sinnvoll, den Overhead des GC zu reduzieren?
Wie immer kommt es darauf an.
Zunächst sollten Sie die GC-Protokollierung aktivieren, um eine klare Übersicht über die Vorgänge zu erhalten. Sie können es mit aktivieren -Xloggc:gc.log -XX:+PrintGCDetails
.
Wenn Ihre Anwendung viel Zeit in einem GC-Zyklus verbringt, optimieren Sie den GC, andernfalls lohnt es sich möglicherweise nicht wirklich.
Wenn Sie beispielsweise alle 100 ms einen jungen GC haben, der 10 ms benötigt, verbringen Sie 10% Ihrer Zeit im GC und Sie haben 10 Sammlungen pro Sekunde (was riesig ist). In einem solchen Fall würde ich keine Zeit mit GC-Tuning verbringen, da diese 10 GC / s immer noch vorhanden wären.
3 - Einige Erfahrungen
Ich hatte ein ähnliches Problem mit einer Anwendung, die eine große Menge einer bestimmten Klasse erstellte. In den GC-Protokollen habe ich festgestellt, dass die Erstellungsrate der Anwendung etwa 3 GB / s betrug, was viel zu viel ist (komm schon ... 3 Gigabyte Daten pro Sekunde ?!).
Das Problem: Zu viele häufige GCs, die durch zu viele erstellte Objekte verursacht werden.
In meinem Fall habe ich einen Speicherprofiler angehängt und festgestellt, dass eine Klasse einen großen Prozentsatz aller meiner Objekte darstellt. Ich habe die Instanziierungen aufgespürt, um herauszufinden, dass es sich bei dieser Klasse im Grunde genommen um ein Paar Boolescher Werte handelt, die in ein Objekt eingewickelt sind. In diesem Fall standen zwei Lösungen zur Verfügung:
Überarbeiten Sie den Algorithmus so, dass ich kein Paar Boolescher Werte zurückgebe, sondern zwei Methoden, die jeden Booleschen Wert separat zurückgeben
Zwischenspeichern Sie die Objekte, da Sie wissen, dass es nur 4 verschiedene Instanzen gibt
Ich entschied mich für die zweite, da sie die Anwendung am wenigsten beeinträchtigte und leicht einzuführen war. Ich habe Minuten gebraucht, um eine Factory mit einem nicht threadsicheren Cache einzurichten (ich brauchte keine Thread-Sicherheit, da ich schließlich nur 4 verschiedene Instanzen haben würde).
Die Zuweisungsrate sank auf 1 GB / s, ebenso wie die Häufigkeit junger GC (geteilt durch 3).
Hoffentlich hilft das !