362 Stimmen

Warum ist es eine schlechte Praxis, System.gc() aufzurufen?

Nach Antwort an eine Frage, wie man Kraftfreie Objekte in Java (der Typ löschte eine 1,5 GB große HashMap) mit System.gc() Mir wurde gesagt, dass es unhöflich sei, die System.gc() manuell, aber die Kommentare waren nicht ganz überzeugend. Außerdem schien es niemand zu wagen, meine Antwort hoch- oder runterzustufen.

Mir wurde dort gesagt, dass es schlechte Praxis ist, aber dann wurde mir auch gesagt, dass Garbage Collector-Läufe nicht mehr systematisch die Welt anhalten, und dass es auch effektiv von der JVM nur als Hinweis verwendet werden könnte, also bin ich irgendwie ratlos.

Ich verstehe, dass die JVM in der Regel besser weiß als Sie, wann sie Speicher zurückfordern muss. Ich verstehe auch, dass es albern ist, sich über ein paar Kilobyte Daten Gedanken zu machen. Ich verstehe auch, dass selbst Megabytes an Daten nicht mehr das sind, was sie vor ein paar Jahren waren. Aber trotzdem, 1,5 Gigabyte? Und Sie savoir Es gibt etwa 1,5 GB an Daten, die im Speicher herumliegen; es ist nicht so, dass es ein Schuss ins Blaue ist. Ist System.gc() systematisch schlecht, oder gibt es einen Punkt, an dem es wieder in Ordnung ist?

Die Frage ist also eigentlich doppelt:

  • Warum ist es eine schlechte Praxis, die System.gc() ? Handelt es sich bei bestimmten Implementierungen wirklich nur um einen Hinweis an die JVM, oder handelt es sich immer um einen vollständigen Sammelzyklus? Gibt es wirklich Garbage-Collector-Implementierungen, die ihre Arbeit erledigen können, ohne die Welt anzuhalten? Bitte erhellen Sie die verschiedenen Behauptungen, die in den Kommentaren zu meiner Antwort .
  • Wo ist die Schwelle? Ist sie jamais eine gute Idee, anzurufen System.gc() oder gibt es Zeiten, in denen das akzeptabel ist? Wenn ja, was sind das für Zeiten?

4 Stimmen

Ich denke, ein guter Zeitpunkt, um System.gc() aufzurufen, ist, wenn Sie etwas tun, das bereits ein langer Ladeprozess ist. Ich arbeite zum Beispiel an einem Spiel und plane, System.gc() aufzurufen, wenn das Spiel einen neuen Level lädt, am Ende des Ladevorgangs. Der Benutzer wartet bereits ein wenig, und die zusätzliche Leistungssteigerung könnte es wert sein; aber ich werde auch eine Option in den Konfigurationsbildschirm aufnehmen, um dieses Verhalten zu deaktivieren.

1 Stimmen

Ein weiterer Fall wurde erläutert in java-monitor.com/forum/showthread.php?t=188 Dort wird erklärt, warum es eine schlechte Praxis sein kann, die Funktion System.gc() aufzurufen.

0 Stimmen

Nur meine zwei Cent, nachdem ich einige der Antworten gelesen habe (und es sollte angemerkt werden, dass ich ziemlich neu in Java bin, also bitte nehmen Sie meine Meinung mit einem Körnchen Salz), aber... Ich denke, die bessere Frage wäre: "Warum haben Sie überhaupt eine so große HashMap?"

257voto

Steven Schlansker Punkte 35955

Der Grund, warum alle immer sagen, man solle die System.gc() ist, dass es sich um eine ein ziemlich guter Indikator für grundlegend fehlerhaften Code . Jeder Code, der aus Gründen der Korrektheit davon abhängt, ist mit Sicherheit fehlerhaft; jeder Code, der aus Gründen der Leistung darauf angewiesen ist, ist höchstwahrscheinlich fehlerhaft.

Sie wissen nicht, mit welcher Art von Garbage Collector Sie arbeiten. Es gibt sicherlich einige, die das nicht tun "Die Welt anhalten" wie Sie behaupten, aber einige JVMs sind nicht so schlau oder tun es aus verschiedenen Gründen (vielleicht sind sie auf einem Telefon?) nicht. Sie wissen nicht, was es tun wird.

Es ist auch nicht garantiert, dass es etwas bewirkt. Die JVM kann Ihre Anfrage einfach komplett ignorieren.

Die Kombination aus "Sie wissen nicht, was es bewirken wird", "Sie wissen nicht, ob es überhaupt hilft" und "Sie brauchen es sowieso nicht anzurufen" ist der Grund, warum die Leute so nachdrücklich sagen, dass Sie es generell nicht anrufen sollten. Ich denke, es ist ein Fall von "wenn du fragen musst, ob du das benutzen solltest, solltest du es nicht tun".


EDIT um auf einige Bedenken aus dem anderen Thread einzugehen:

Nachdem ich den von Ihnen verlinkten Thread gelesen habe, möchte ich noch auf ein paar Dinge hinweisen. Erstens hat jemand vorgeschlagen, dass der Aufruf gc() kann dem System Speicherplatz zurückgeben. Das ist nicht unbedingt richtig - der Java-Heap selbst wächst unabhängig von Java-Zuweisungen.

Das bedeutet, dass die JVM Speicher (mehrere Dutzend Megabyte) vorhält und den Heap bei Bedarf erweitert. Sie gibt diesen Speicher nicht unbedingt an das System zurück, selbst wenn Sie Java-Objekte freigeben; es steht ihr völlig frei, den zugewiesenen Speicher für zukünftige Java-Zuweisungen zu behalten.

Um zu zeigen, dass es möglich ist, dass System.gc() tut nichts, Ansicht JDK-Fehler 6668279 und vor allem, dass es eine -XX:DisableExplicitGC VM-Option:

Standardmäßig werden Aufrufe an System.gc() aktiviert sind ( -XX:-DisableExplicitGC ). Verwenden Sie . -XX:+DisableExplicitGC zum Deaktivieren von Aufrufen an System.gc() . Beachten Sie, dass die JVM bei Bedarf weiterhin eine Garbage Collection durchführt.

1 Stimmen

Vielleicht können Sie ein Wehr bauen

2 Stimmen

@

0 Stimmen

A

152voto

Stephen C Punkte 665668

Es wurde bereits erläutert, dass der Aufruf system.gc() Mai nichts tun, und dass jeder Code, der den Garbage Collector "braucht", um zu laufen, kaputt ist.

Der pragmatische Grund dafür, dass es eine schlechte Praxis ist, die System.gc() ist, dass sie ineffizient ist. Und im schlimmsten Fall ist es völlig ineffizient ! Lassen Sie mich das erklären.

Ein typischer GC-Algorithmus identifiziert Garbage, indem er alle Nicht-Garbage-Objekte im Heap durchläuft und daraus ableitet, dass jedes nicht besuchte Objekt Garbage sein muss. Daraus können wir modellieren, dass die Gesamtarbeit einer Garbage Collection aus einem Teil besteht, der proportional zur Menge der Live-Daten ist, und einem anderen Teil, der proportional zur Menge des Garbage ist, d.h. work = (live * W1 + garbage * W2) .

Nehmen wir nun an, dass Sie in einer Single-Thread-Anwendung Folgendes tun.

System.gc(); System.gc();

Der erste Aufruf wird (unserer Meinung nach) Folgendes tun (live * W1 + garbage * W2) Arbeit und die Beseitigung des anfallenden Mülls.

Der zweite Aufruf macht (live* W1 + 0 * W2) arbeiten und nichts zurückfordern. Mit anderen Worten: Wir haben getan (live * W1) Arbeit und absolut nichts erreicht .

Wir können die Effizienz des Collectors als den Arbeitsaufwand modellieren, der erforderlich ist, um eine Einheit Müll zu sammeln, d. h. efficiency = (live * W1 + garbage * W2) / garbage . Um die GC so effizient wie möglich zu gestalten, müssen wir also maximieren den Wert von garbage wenn wir die GC ausführen, d.h. warten, bis der Heap voll ist. (Außerdem sollte der Heap so groß wie möglich sein, aber das ist ein anderes Thema).

Wenn die Anwendung nicht eingreift (durch Aufruf von System.gc() ), wird die GC warten, bis der Heap voll ist, bevor sie ausgeführt wird, was zu einer effizienten Sammlung von Müll führt 1 . Wenn aber die Anwendung die GC erzwingt, ist die Wahrscheinlichkeit groß, dass der Heap nicht voll ist, und das Ergebnis ist, dass Müll ineffizient gesammelt wird. Und je öfter die Anwendung die GC erzwingt, desto ineffizienter wird die GC.

Anmerkung: Die obige Erklärung vernachlässigt die Tatsache, dass eine typische moderne GC den Heap in "Spaces" unterteilt, dass die GC den Heap dynamisch erweitern kann, dass die Arbeitsmenge der Nicht-Garbage-Objekte der Anwendung variieren kann und so weiter. Dennoch gilt das gleiche Grundprinzip für alle echten Garbage Collectors 2 . Es ist ineffizient, den GC zu zwingen, zu laufen.


1 - So funktioniert der "Durchsatz"-Sammler. Concurrent Collectors wie CMS und G1 verwenden unterschiedliche Kriterien, um zu entscheiden, wann sie den Garbage Collector starten.

2 - Ich schließe auch Speichermanager aus, die ausschließlich Referenzzählung verwenden, aber keine aktuelle Java-Implementierung verwendet diesen Ansatz ... aus gutem Grund.

47 Stimmen

+1 Gute Erklärung. Beachten Sie jedoch, dass diese Argumentation nur gilt, wenn Sie sich um den Durchsatz kümmern. Wenn Sie die Latenz an bestimmten Punkten optimieren wollen, kann es sinnvoll sein, GC zu erzwingen. Z.B. (hypothetisch gesprochen) könnte man in einem Spiel Verzögerungen während der Levels vermeiden wollen, aber man kümmert sich nicht um Verzögerungen beim Laden der Levels. Dann wäre es sinnvoll, GC nach dem Laden des Levels zu erzwingen. Das verringert zwar den Gesamtdurchsatz, aber das ist ja nicht das, was Sie optimieren wollen.

3 Stimmen

@sleske - was Sie sagen, ist wahr. Die GC zwischen den Levels laufen zu lassen, ist jedoch eine Notlösung ... und löst das Latenzproblem nicht, wenn die Levels so lange dauern, dass man die GC sowieso während eines Levels laufen lassen muss. Ein besserer Ansatz ist die Verwendung eines gleichzeitigen (pausenarmen) Garbage Collectors ... wenn die Plattform dies unterstützt.

0 Stimmen

Ich bin mir nicht ganz sicher, was Sie mit "braucht den Garbage Collector zum Laufen" meinen. Die Bedingungen, die Anwendungen vermeiden müssen, sind: Allokationsfehler, die nie befriedigt werden können, und hoher GC-Overhead. Zufällige Aufrufe von System.gc() führen oft zu einem hohen GC-Overhead.

51voto

KitsuneYMG Punkte 12477

Viele Leute scheinen Ihnen zu raten, dies nicht zu tun. Ich bin da anderer Meinung. Wenn Sie nach einem großen Ladevorgang wie dem Laden eines Levels glauben, dass:

  1. Sie haben viele Objekte, die unerreichbar sind und möglicherweise nicht gc'ed wurden. und
  2. Glauben Sie, dass der Benutzer an dieser Stelle eine kleine Verlangsamung in Kauf nehmen kann?

Es schadet nicht, System.gc() aufzurufen. Ich betrachte es wie die C/C++ inline Stichwort. Es ist nur ein Hinweis für den gc, dass Sie, der Entwickler, entschieden haben, dass Zeit/Leistung nicht so wichtig ist wie sonst und dass ein Teil davon für die Rückgewinnung von Speicher verwendet werden könnte.

Der Ratschlag, sich nicht darauf zu verlassen, dass es etwas bewirkt, ist richtig. Verlassen Sie sich nicht darauf, dass es funktioniert, aber der Hinweis, dass jetzt ein akzeptabler Zeitpunkt zum Sammeln ist, ist völlig in Ordnung. Ich würde lieber Zeit an einem Punkt im Code verschwenden, an dem es keine Rolle spielt (Ladebildschirm), als wenn der Benutzer aktiv mit dem Programm interagiert (z. B. während eines Levels in einem Spiel).

Es gibt eine Zeit, da werde ich Kraft Sammlung: bei dem Versuch, herauszufinden, ist ein bestimmtes Objekt Lecks (entweder native Code oder große, komplexe Callback-Interaktion. Oh und jede UI-Komponente, die auch nur einen Blick auf Matlab wirft). Dies sollte niemals in Produktionscode verwendet werden.

4 Stimmen

+1 für GC bei der Analyse von Speicherlecks. Beachten Sie, dass die Informationen über die Heap-Nutzung (Runtime.freeMemory() u.a.) nur sinnvoll sind, nachdem eine GC erzwungen wurde, da sie sonst davon abhängen würden, wann das System zuletzt eine GC durchgeführt hat.

4 Stimmen

es kann nicht schaden, System.gc() aufzurufen es könnte erforderlich sein stop the world Ansatz, und das ist ein echter Schaden, wenn es passiert

7 Stimmen

Dort est schadet es nicht, den Garbage Collector explizit aufzurufen. Durch den Aufruf des GC zur falschen Zeit werden CPU-Zyklen verschwendet. Sie (der Programmierer) verfügen nicht über genügend Informationen, um zu bestimmen, wann der richtige Zeitpunkt ist ... aber die JVM schon.

36voto

JT. Punkte 361

Die Leute haben gut erklärt, warum man es NICHT verwenden sollte, also werde ich Ihnen ein paar Situationen nennen, in denen Sie es verwenden sollten:

(Die folgenden Kommentare beziehen sich auf Hotspot unter Linux mit dem CMS-Kollektor, wo ich mich sicher fühle, dass System.gc() in der Tat immer eine vollständige Garbage Collection auslöst).

  1. Nach der anfänglichen Arbeit der Inbetriebnahme Ihrer Anwendung, können Sie einen schrecklichen Zustand der Speichernutzung sein. Die Hälfte Ihrer "Tenured Generation" könnte voller Müll sein, was bedeutet, dass Sie Ihrem ersten CMS ein gutes Stück näher gekommen sind. In Anwendungen, bei denen das eine Rolle spielt, ist es keine schlechte Idee, System.gc() aufzurufen, um den Heap auf den Ausgangszustand mit lebenden Daten "zurückzusetzen".

  2. Ähnlich wie bei Nr. 1: Wenn Sie die Heap-Nutzung genau überwachen, möchten Sie einen genauen Überblick über Ihre grundlegende Speichernutzung haben. Wenn die ersten 2 Minuten der Betriebszeit Ihrer Anwendung nur aus Initialisierung bestehen, werden Ihre Daten durcheinander gebracht, es sei denn, Sie erzwingen (ähm... "empfehlen") den vollen gc im Voraus.

  3. Möglicherweise haben Sie eine Anwendung, die so konzipiert ist, dass sie während ihrer Laufzeit nie etwas an die "Tenured Generation" weitergibt. Aber vielleicht müssen Sie einige Daten im Voraus initialisieren, die nicht so groß sind, dass sie automatisch in die Tenured-Generation verschoben werden. Wenn Sie nicht System.gc() aufrufen, nachdem alles eingerichtet ist, könnten Ihre Daten in der neuen Generation verbleiben, bis es an der Zeit ist, sie zu verschieben. Plötzlich wird Ihre super-duper Low-Latency, Low-GC-Anwendung mit einer RIESIGEN (relativ gesehen, natürlich) Latenzstrafe für das Promoten dieser Objekte während des normalen Betriebs getroffen.

  4. Es ist manchmal nützlich, einen System.gc-Aufruf in einer Produktionsanwendung zur Verfügung zu haben, um die Existenz eines Speicherlecks zu überprüfen. Wenn Sie wissen, dass die Menge der Live-Daten zum Zeitpunkt X in einem bestimmten Verhältnis zu der Menge der Live-Daten zum Zeitpunkt Y stehen sollte, kann es nützlich sein, System.gc() zum Zeitpunkt X und zum Zeitpunkt Y aufzurufen und die Speichernutzung zu vergleichen.

1 Stimmen

Bei den meisten Generations-Garbage-Collectors müssen die Objekte der neuen Generation eine bestimmte (oft konfigurierbare) Anzahl von Garbage-Collections überstehen, so dass der Aufruf von System.gc() einmal die Beförderung von Objekten zu erzwingen, bringt Ihnen nichts. Und Sie wollen sicher nicht System.gc() achtmal hintereinander und beten, dass die Aktion nun abgeschlossen ist und die eingesparten Kosten einer späteren Aktion die Kosten für mehrere volle GCs rechtfertigen. Je nach GC-Algorithmus verursacht das Heraufstufen vieler Objekte nicht einmal tatsächliche Kosten, da der Speicher einfach der alten Generation neu zugewiesen oder gleichzeitig kopiert wird

14voto

Dmitry Punkte 4722

Das ist eine sehr lästige Frage, und ich glaube, sie trägt dazu bei, dass viele gegen Java sind, obwohl es eine sehr nützliche Sprache ist.

Die Tatsache, dass man sich nicht darauf verlassen kann, dass "System.gc" irgendetwas tut, ist unglaublich entmutigend und kann leicht ein Gefühl von "Fear, Uncertainty, Doubt" in der Sprache hervorrufen.

In vielen Fällen ist es gut, mit Speicherspitzen umzugehen, die Sie absichtlich verursachen, bevor ein wichtiges Ereignis eintritt, das bei den Benutzern den Eindruck erwecken würde, Ihr Programm sei schlecht konzipiert/unreaktionsfähig.

Die Möglichkeit, die Garbage Collection zu kontrollieren, wäre ein großartiges Bildungsinstrument, das wiederum das Verständnis der Menschen für die Funktionsweise der Garbage Collection verbessern würde und ihnen zeigen würde, wie sie Programme erstellen können, die sowohl das Standardverhalten als auch das kontrollierte Verhalten ausnutzen.

Ich möchte noch einmal auf die Argumente in diesem Thread eingehen.

  1. Sie ist ineffizient:

Oft tut das Programm gar nichts, und Sie wissen, dass es nichts tut, weil es so konzipiert wurde. Es könnte zum Beispiel eine lange Wartezeit mit einer großen Warte-Meldungsbox durchlaufen, und am Ende könnte es einen Aufruf zum Sammeln von Müll hinzufügen, weil die Zeit, um ihn auszuführen, nur einen Bruchteil der Zeit der langen Wartezeit benötigt, aber verhindert, dass gc mitten in einer wichtigeren Operation versagt.

  1. Das ist immer eine schlechte Praxis und deutet auf fehlerhaften Code hin.

Ich bin anderer Meinung, es ist egal, welchen Garbage Collector Sie haben. Seine Aufgabe ist es, Müll aufzuspüren und ihn zu beseitigen.

Indem Sie gc zu Zeiten aufrufen, in denen die Nutzung weniger kritisch ist, verringern Sie die Wahrscheinlichkeit, dass es ausgeführt wird, wenn Ihr Leben von der Ausführung des spezifischen Codes abhängt, aber stattdessen beschließt es, Müll zu sammeln.

Sicher, es verhält sich vielleicht nicht so, wie Sie es wollen oder erwarten, aber wenn Sie es aufrufen wollen, wissen Sie, dass nichts passiert, und der Benutzer ist bereit, die Verlangsamung/Ausfallzeit zu tolerieren. Wenn das System.gc funktioniert, großartig! Wenn nicht, dann haben Sie es wenigstens versucht. Es gibt einfach keinen Nachteil, es sei denn, der Garbage Collector hat inhärente Seiteneffekte, die etwas schrecklich Unerwartetes tun, wie sich ein Garbage Collector verhalten sollte, wenn er manuell aufgerufen wird, und das allein verursacht schon Misstrauen.

  1. Dies ist kein üblicher Anwendungsfall:

Es handelt sich um einen Anwendungsfall, der nicht zuverlässig erreicht werden kann, der aber möglich wäre, wenn das System so konzipiert wäre. Es ist wie eine Ampel und machen es so, dass einige / alle der Ampel "Tasten tun nichts, es macht Sie Frage, warum die Taste gibt es mit zu beginnen, Javascript hat keine Garbage Collection Funktion, so dass wir es nicht so viel für sie hinterfragen.

  1. Die Spezifikation besagt, dass System.gc() ein Hinweis darauf ist, dass GC ausgeführt werden soll, und dass es der VM freisteht, ihn zu ignorieren.

was ist ein "hinweis"? was ist "ignorieren"? ein computer kann nicht einfach hinweise annehmen oder etwas ignorieren, es gibt strenge Verhaltensweisen, die dynamisch sein können und von der Absicht des Systems geleitet werden. Eine angemessene Antwort würde beinhalten, was der Garbage Collector auf der Implementierungsebene tatsächlich tut, das ihn dazu veranlasst, die Sammlung nicht durchzuführen, wenn Sie sie anfordern. Ist die Funktion einfach ein Nop? Gibt es bestimmte Bedingungen, die erfüllt werden müssen? Wie lauten diese Bedingungen?

In seiner jetzigen Form wirkt der GC von Java oft wie ein Monster, dem man einfach nicht trauen kann. Man weiß nicht, wann er kommt oder geht, man weiß nicht, was er tun wird und wie er es tun wird. Ich kann mir vorstellen, dass einige Experten eine bessere Vorstellung davon haben, wie ihre Garbage Collection auf Befehlsbasis funktioniert, aber die große Mehrheit hofft einfach, dass sie "einfach funktioniert", und einem undurchsichtig erscheinenden Algorithmus vertrauen zu müssen, dass er die Arbeit für einen erledigt, ist frustrierend.

Es ist ein großer Unterschied, ob man über etwas liest oder etwas beigebracht bekommt, oder ob man die tatsächliche Umsetzung sieht, die Unterschiede zwischen den verschiedenen Systemen und die Möglichkeit, damit zu spielen, ohne den Quellcode ansehen zu müssen. Das schafft Vertrauen und das Gefühl von Beherrschung/Verständnis/Kontrolle.

Zusammenfassend lässt sich sagen, dass es ein inhärentes Problem mit den Antworten "diese Funktion könnte nichts tun, und ich werde nicht ins Detail gehen, wie man erkennt, wann sie etwas tut und wann nicht und warum sie es tut oder nicht tut, was oft impliziert, dass es einfach gegen die Philosophie ist, es zu versuchen, selbst wenn die Absicht dahinter vernünftig ist".

Es mag in Ordnung sein, dass sich die Java GC so verhält, wie sie es tut, oder auch nicht, aber um sie zu verstehen, ist es schwierig, wirklich nachzuvollziehen, in welche Richtung man gehen muss, um einen umfassenden Überblick darüber zu bekommen, was man der GC zutrauen kann und was nicht, also ist es zu einfach, der Sprache zu misstrauen, denn der Zweck einer Sprache ist es, ein kontrolliertes Verhalten bis zu einem philosophischen Ausmaß zu haben (es ist leicht für einen Programmierer, (es ist leicht für einen Programmierer, besonders für einen Anfänger, in eine existenzielle Krise zu geraten, wenn bestimmte Verhaltensweisen des Systems/der Sprache), die man tolerieren kann (und wenn man das nicht kann, wird man die Sprache einfach nicht benutzen, bis man es muss), und mehr Dinge, die man nicht kontrollieren kann, ohne dass man weiß, warum man sie nicht kontrollieren kann, sind von Natur aus schädlich.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X