Obwohl ich die schwerwiegenden Auswirkungen des Spielens mit dieser Funktion verstehe (oder zumindest denke ich das), verstehe ich nicht, warum es zu einem dieser Dinge wird, die seriöse Programmierer niemals verwenden würden, selbst diejenigen, die es nicht einmal wissen Wofür ist es.
Angenommen, ich entwickle eine Anwendung, bei der die Speichernutzung je nach Benutzer stark variiert. Der Anwendungslebenszyklus kann in zwei Hauptphasen unterteilt werden: Bearbeitung und Echtzeitverarbeitung. Angenommen, während der Bearbeitungsphase werden Milliarden oder sogar Billionen von Objekten erstellt. Einige von ihnen sind klein und einige nicht, andere haben möglicherweise Finalisierer und andere nicht und nehmen an, dass ihre Lebensdauer von wenigen Millisekunden bis zu langen Stunden variiert. Als nächstes beschließt der Benutzer, zur Echtzeitstufe zu wechseln. Nehmen wir an dieser Stelle an, dass die Leistung eine grundlegende Rolle spielt und die geringste Änderung des Programmflusses katastrophale Folgen haben könnte. Die Objekterstellung wird dann durch die Verwendung von Objektpools und dergleichen auf ein Minimum reduziert. Dann mischt sich der GC unerwartet ein und wirft alles weg, und jemand stirbt.
Die Frage: In diesem Fall wäre es nicht ratsam, GC.Collect () aufzurufen, bevor Sie die zweite Stufe betreten?
Schließlich überschneiden sich diese beiden Phasen nie zeitlich miteinander, und alle Optimierungen und Statistiken, die der GC hätte sammeln können, wären hier von geringem Nutzen ...
Hinweis: Wie einige von Ihnen bereits betont haben, ist .NET möglicherweise nicht die beste Plattform für eine solche Anwendung, aber das geht über den Rahmen dieser Frage hinaus. Damit soll geklärt werden, ob ein Aufruf von GC.Collect () das allgemeine Verhalten / die Leistung einer Anwendung verbessern kann oder nicht. Wir sind uns alle einig, dass die Umstände, unter denen Sie so etwas tun würden, äußerst selten sind, aber andererseits versucht der GC zu raten und macht es die meiste Zeit perfekt, aber es geht immer noch darum, zu raten.
Vielen Dank.
Antworten:
Aus Ricos Blog ...
Es hört sich also so an, als ob diese Situation unter Regel 2 fallen könnte. Sie wissen, dass es einen Moment gibt, in dem viele alte Objekte gestorben sind und es nicht wiederkehrend ist. Vergessen Sie jedoch nicht Ricos Abschiedsworte.
Messen, messen, messen.
quelle
Wenn Sie GC.Collect () im Produktionscode aufrufen, erklären Sie im Wesentlichen, dass Sie mehr wissen als die Autoren des GC. Das kann der Fall sein. Dies ist jedoch normalerweise nicht der Fall und wird daher dringend empfohlen.
quelle
malloc
nur eine sehr einfache Oberfläche und die Implementierungen können so unterschiedlich sein, dass sie eine Rolle spielen. Es hängt alles davon ab, welche Art von Problem Sie lösen möchten. Wennmalloc
"gut genug" wäre, würden Sie kein Puffer-Pooling benötigen, oder? Die C / C ++ - Entwicklung ist voll von Beispielen, in denen Sie versuchen, das Betriebssystem / die Laufzeit / die Bibliotheken zu überdenken, weil Sie es besser wissen (und manchmal wirklich). Viele leistungskritische Anwendungen vermeiden die Verwendung von System- / Laufzeitzuordnungen vollständig. Spiele, mit denen der gesamte Speicher beim Start vorab zugewiesen wird (Arrays mit konstanter Größe usw.).Wie wäre es also, wenn Sie COM-Objekte wie MS Word oder MS Excel aus .NET verwenden? Ohne Aufruf
GC.Collect
nach dem Freigeben der COM-Objekte haben wir festgestellt, dass die Word- oder Excel-Anwendungsinstanzen noch vorhanden sind.Tatsächlich verwenden wir folgenden Code:
Wäre das also eine falsche Verwendung des Garbage Collectors? Wenn ja, wie bringen wir die Interop-Objekte zum Sterben? Auch wenn es nicht gemeint ist , wie diese verwendet werden soll, warum die
GC
‚sCollect
Methode selbstPublic
?quelle
Nun, der GC ist eines der Dinge, mit denen ich eine Hassliebe habe. Wir haben es in der Vergangenheit durch VistaDB gebrochen und darüber gebloggt. Sie haben es behoben, aber es dauert lange, bis sie Korrekturen für solche Dinge erhalten.
Der GC ist komplex und ein One-Size-Fits-All-Ansatz ist sehr, sehr schwer für etwas so Großes zu realisieren. MS hat ziemlich gute Arbeit geleistet, aber es ist manchmal möglich, den GC zu täuschen.
Im Allgemeinen sollten Sie keine hinzufügen, es
Collect
sei denn, Sie wissen, dass Sie gerade eine Tonne Speicherplatz abgeladen haben, und es kommt zu einer Mid-Life-Krise, wenn der GC sie jetzt nicht bereinigt.Sie können die gesamte Maschine mit einer Reihe von schlechten
GC.Collect
Aussagen vermasseln. Die Notwendigkeit einer Collect-Anweisung weist fast immer auf einen größeren zugrunde liegenden Fehler hin. Der Speicherverlust hat normalerweise mit Referenzen und mangelndem Verständnis für deren Funktionsweise zu tun. Oder verwenden Sie dieIDisposable
On-Objekte, die sie nicht benötigen, und belasten Sie den GC erheblich.Beobachten Sie genau, wie viel Prozent der Zeit in GC über die Systemleistungsindikatoren verbracht wurden. Wenn Sie sehen, dass Ihre App 20% oder mehr ihrer Zeit im GC verbraucht, treten schwerwiegende Probleme bei der Objektverwaltung (oder ein abnormales Nutzungsmuster) auf. Sie möchten die Zeit, die der GC verbringt, immer minimieren, da dies Ihre gesamte App beschleunigt.
Es ist auch wichtig zu beachten, dass sich der GC auf Servern von Workstations unterscheidet. Ich habe eine Reihe kleiner, schwer zu findender Probleme mit Leuten gesehen, die nicht beide getestet haben (oder nicht einmal wissen, dass es sich um zwei handelt).
Und um in meiner Antwort so vollständig wie möglich zu sein, sollten Sie auch unter Mono testen, ob Sie auch auf diese Plattform abzielen. Da es sich um eine völlig andere Implementierung handelt, können völlig andere Probleme auftreten als bei der MS-Implementierung.
quelle
Es gibt Situationen, in denen es nützlich ist, aber im Allgemeinen sollte es vermieden werden. Sie könnten es mit GOTO vergleichen oder ein Moped fahren: Sie tun es, wenn Sie müssen, aber Sie erzählen Ihren Freunden nichts davon.
quelle
Aus meiner Erfahrung war es nie ratsam, GC.Collect () im Produktionscode aufzurufen. Ja, beim Debuggen hat es seine Vorteile, potenzielle Speicherlecks zu klären. Ich denke, mein grundlegender Grund ist, dass der GC von Programmierern viel intelligenter als ich geschrieben und optimiert wurde, und wenn ich zu einem Punkt komme, an dem ich das Gefühl habe, GC.Collect () aufrufen zu müssen, ist dies ein Hinweis darauf, dass ich vom Weg abgekommen bin irgendwo. In Ihrer Situation hört es sich nicht so an, als hätten Sie tatsächlich Speicherprobleme, nur dass Sie sich Sorgen machen, welche Instabilität die Sammlung für Ihren Prozess mit sich bringt. Angesichts der Tatsache, dass noch verwendete Objekte nicht gereinigt werden und dass sie sich sehr schnell an steigende und sinkende Anforderungen anpassen, würde ich denken, dass Sie sich darüber keine Sorgen machen müssen.
quelle
Einer der Hauptgründe für den Aufruf von GC.Collect () ist, dass Sie gerade ein bedeutendes Ereignis ausgeführt haben, das viel Müll erzeugt, wie Sie es beschreiben. Das Aufrufen von GC.Collect () kann hier eine gute Idee sein. Andernfalls versteht der GC möglicherweise nicht, dass es sich um ein "einmaliges" Ereignis handelt.
Natürlich sollten Sie es profilieren und selbst sehen.
quelle
Natürlich sollten Sie keinen Code mit Echtzeitanforderungen in Sprachen mit nicht in Echtzeit verfügbarer Speicherbereinigung schreiben.
In einem Fall mit genau definierten Phasen gibt es kein Problem beim Auslösen des Garbage Collector. Dieser Fall ist jedoch äußerst selten. Das Problem ist, dass viele Entwickler versuchen werden, dies zu verwenden, um Probleme in einem Frachtkultstil zu überarbeiten, und das wahllose Hinzufügen führt zu Leistungsproblemen.
quelle
Durch Aufrufen von GC.Collect () wird die CLR gezwungen, einen Stapelspaziergang durchzuführen, um festzustellen, ob jedes Objekt durch Überprüfen der Referenzen wirklich freigegeben werden kann. Dies wirkt sich auf die Skalierbarkeit aus, wenn die Anzahl der Objekte hoch ist, und es ist auch bekannt, dass die Speicherbereinigung zu häufig ausgelöst wird. Vertrauen Sie der CLR und lassen Sie den Garbage Collector bei Bedarf selbst ausführen.
quelle
Tatsächlich halte ich es nicht für eine sehr schlechte Praxis, GC.Collect anzurufen.
Es kann Fälle geben, in denen wir das brauchen. Zum Beispiel habe ich ein Formular, das einen Thread ausführt, der verschiedene Tabellen in einer Datenbank öffnet, den Inhalt in einem BLOB-Feld in eine temporäre Datei extrahiert, die Datei verschlüsselt, die Datei dann in einen Binärstream und zurück in ein BLOB liest Feld in einer anderen Tabelle.
Der gesamte Vorgang benötigt ziemlich viel Speicher und es ist nicht sicher, wie viele Zeilen und wie groß der Dateiinhalt in den Tabellen ist.
Ich habe oft die OutofMemory-Ausnahme erhalten und dachte, es wäre ratsam, GC.Collect regelmäßig basierend auf einer Zählervariablen auszuführen. Ich inkrementiere einen Zähler und wenn ein bestimmtes Niveau erreicht ist, wird GC aufgerufen, um eventuell entstandenen Müll zu sammeln und jeglichen Speicherverlust zurückzugewinnen, der aufgrund unvorhergesehener Speicherlecks verloren gegangen ist.
Danach denke ich, dass es gut funktioniert, zumindest keine Ausnahme !!!
Ich rufe folgendermaßen an:
quelle
Unter .net hängt die Zeit, die zum Ausführen einer Speicherbereinigung erforderlich ist, viel stärker von der Menge der Dinge ab, die kein Müll sind, als von der Menge der Dinge, die es sind. In der Tat ist es das einzige, was den Speicher identifiziert, in dem es sich befindet , es sei denn, ein Objekt überschreibt
Finalize
(entweder explizit oder über den C # -Destruktor), ist das Ziel von aWeakReference
, befindet sich auf dem Heap für große Objekte oder ist auf eine andere gc-bezogene Weise speziell als Objekt ist die Existenz verwurzelter Verweise darauf. Ansonsten ist der Betrieb des GC analog dazu, einem Gebäude alles Wertvolle abzunehmen, das Gebäude zu dynamisieren, ein neues an der Stelle des alten zu errichten und alle wertvollen Gegenstände darin abzulegen. Der Aufwand für die Dynamisierung des Gebäudes ist völlig unabhängig von der Menge des darin enthaltenen Mülls.Infolgedessen kann das Anrufen
GC.Collect
den Gesamtaufwand für das System erhöhen. Dies verzögert das Auftreten der nächsten Sammlung, erledigt jedoch wahrscheinlich sofort genau so viel Arbeit, wie die nächste Sammlung zum Zeitpunkt ihres Auftretens erforderlich gemacht hätte. Zu dem Zeitpunkt, an dem die nächste Sammlung stattgefunden hätte, war die Gesamtzeit, die für das Sammeln aufgewendet wurde, ungefähr dieselbe wieGC.Collect
nicht aufgerufen, aber das System hat etwas Müll angesammelt, was dazu führte, dass die nachfolgende Sammlung früher alsGC.Collect
bisher benötigt wurde wurde gerufen.Die Zeiten, in denen ich sehen kann, dass sie
GC.Collect
wirklich nützlich sind, sind, wenn man entweder die Speichernutzung eines Codes messen muss (da die Zahlen zur Speichernutzung nur nach einer Sammlung wirklich aussagekräftig sind) oder profilieren muss, welcher von mehreren Algorithmen besser ist (Aufruf von GC.Collect () Bevor Sie einen von mehreren Codeteilen ausführen, können Sie einen konsistenten Basisstatus sicherstellen. Es gibt einige andere Fälle, in denen man Dinge weiß, die der GC nicht weiß, aber wenn man kein Single-Thread-Programm schreibt, kann man nicht wissen, dass einGC.Collect
Aufruf, der den Datenstrukturen eines Threads helfen würde, eine "Mid-Life-Krise" zu vermeiden "würde nicht dazu führen, dass die Daten anderer Threads eine" Mid-Life-Krise "haben, die sonst vermieden worden wäre.quelle
Erstellen von Bildern in einer Schleife - selbst wenn Sie dispose aufrufen, wird der Speicher nicht wiederhergestellt. Müll sammeln jedes Mal. Ich habe von 1,7 GB Speicher in meiner Fotoverarbeitungs-App auf 24 MB umgestellt und die Leistung ist ausgezeichnet.
Es gibt absolut Zeit, die Sie benötigen, um GC.Collect anzurufen.
quelle
Dispose
soll keinen verwalteten Speicher freigeben. Sie scheinen nicht zu wissen, wie das Speichermodell in .NET funktioniert.Wir hatten ein ähnliches Problem mit dem Garbage Collector, der keinen Müll sammelt und Speicher freigibt.
In unserem Programm haben wir einige Excel-Tabellen mit bescheidener Größe mit OpenXML verarbeitet. Die Tabellen enthielten zwischen 5 und 10 "Blätter" mit etwa 1000 Zeilen mit 14 Spalten.
Das Programm in einer 32-Bit-Umgebung (x86) würde mit einem Fehler "Nicht genügend Speicher" abstürzen. Wir haben es in einer x64-Umgebung zum Laufen gebracht, aber wir wollten eine bessere Lösung.
Wir haben einen gefunden.
Hier sind einige vereinfachte Codefragmente dessen, was nicht funktioniert hat und was funktioniert hat, wenn der Garbage Collector explizit aufgerufen wird, um Speicher von entsorgten Objekten freizugeben.
Das Aufrufen des GC innerhalb des Unterprogramms funktionierte nicht. Erinnerung wurde nie zurückgefordert ...
Durch Verschieben des GC-Aufrufs außerhalb des Bereichs der Unterroutine wurde der Müll gesammelt und der Speicher freigegeben.
Ich hoffe, dies hilft anderen, die mit der .NET-Speicherbereinigung frustriert sind, wenn sie die Aufrufe von zu ignorieren scheint
GC.Collect()
.Paul Smith
quelle
Es ist nichts Falsches daran, explizit eine Sammlung anzufordern. Einige Leute wollen einfach nur glauben, dass es sich nicht um eine Dienstleistung handelt, die vom Anbieter bereitgestellt wird. Oh, und all diese zufälligen Einfrierungen im falschen Moment Ihrer interaktiven Anwendung? Die nächste Version wird es besser machen!
Wenn ein Hintergrundprozess sich mit Speichermanipulation befasst, muss er sich nicht selbst darum kümmern. Dies bedeutet jedoch nicht logischerweise, dass es für uns am besten ist, nicht unter allen Umständen selbst damit umzugehen. Der GC ist für die meisten Fälle optimiert. Dies bedeutet jedoch nicht logisch, dass es in allen Fällen optimiert ist.
Haben Sie jemals eine offene Frage wie "Welcher ist der beste Sortieralgorithmus?" Mit einer endgültigen Antwort beantwortet? Wenn ja, berühren Sie den GC nicht. Für diejenigen unter Ihnen, die nach den Bedingungen gefragt oder Antworten in diesem Fall gegeben haben, können Sie sich über den GC und den Zeitpunkt seiner Aktivierung informieren.
Ich muss sagen, ich hatte Anwendungsstopps in Chrome und Firefox, die mich verdammt frustrieren, und selbst dann wächst in einigen Fällen der Speicher ungehindert - wenn sie nur lernen würden, den Garbage Collector anzurufen - oder gaben mir einen Klicken Sie auf die Schaltfläche, damit ich beim Lesen des Texts einer Seite darauf klicken und die nächsten 20 Minuten frei von Einfrierungen sein kann.
quelle
Ich denke, Sie haben Recht mit dem Szenario, aber ich bin mir über die API nicht sicher.
Microsoft sagt, dass Sie in solchen Fällen Speicherdruck hinzufügen sollten, um dem GC anzuzeigen, dass er bald eine Sammlung durchführen soll.
quelle
Was stimmt damit nicht? Die Tatsache, dass Sie den Garbage Collector und den Speicherzuweiser hinterfragen, die eine viel bessere Vorstellung von der tatsächlichen Speichernutzung Ihrer Anwendung zur Laufzeit haben als Sie.
quelle
Der Wunsch, GC.Collect () aufzurufen, versucht normalerweise, Fehler zu vertuschen, die Sie woanders gemacht haben!
Es wäre besser, wenn Sie herausfinden würden, wo Sie vergessen haben, Dinge zu entsorgen, die Sie nicht mehr benötigen.
quelle
Unter dem Strich können Sie die Anwendung profilieren und sehen, wie sich diese zusätzlichen Sammlungen auf die Dinge auswirken. Ich würde jedoch vorschlagen, sich davon fernzuhalten, es sei denn, Sie werden ein Profil erstellen. Der GC ist so konzipiert, dass er für sich selbst sorgt. Wenn sich die Laufzeit weiterentwickelt, können sie die Effizienz steigern. Sie möchten nicht, dass eine Menge Code herumhängt, der die Arbeit durcheinander bringt und diese Verbesserungen nicht nutzen kann. Es gibt ein ähnliches Argument für die Verwendung von foreach anstelle von for, dh, dass zukünftige Verbesserungen unter dem Deckmantel zu foreach hinzugefügt werden können und Ihr Code sich nicht ändern muss, um die Vorteile zu nutzen.
quelle
Das .NET Framework selbst wurde nie für die Ausführung in einer Echtzeitumgebung entwickelt. Wenn Sie wirklich eine Echtzeitverarbeitung benötigen, verwenden Sie entweder eine eingebettete Echtzeitsprache, die nicht auf .NET basiert, oder das .NET Compact Framework, das auf einem Windows CE-Gerät ausgeführt wird.
quelle
Das Schlimmste ist, dass Ihr Programm ein wenig einfriert. Also, wenn das für Sie in Ordnung ist, tun Sie es. Normalerweise wird es nicht für Thick Client- oder Web-Apps mit meistens Benutzerinteraktion benötigt.
Ich habe festgestellt, dass manchmal Programme mit lang laufenden Threads oder Stapelverarbeitungsprogramme eine OutOfMemory-Ausnahme erhalten, obwohl sie Objekte ordnungsgemäß entsorgen. Eine, an die ich mich erinnere, war eine Transaktionsverarbeitung für Branchendatenbanken. Die andere war eine Indizierungsroutine für einen Hintergrundthread in einer Thick-Client-App.
In beiden Fällen war das Ergebnis einfach: Kein GC.Collect, nicht genügend Speicher, konsistent; GC.Collect, einwandfreie Leistung.
Ich habe es mehrmals versucht, um Speicherprobleme zu lösen, ohne Erfolg. Ich habe es herausgenommen.
Kurz gesagt, setzen Sie es nicht ein, es sei denn, Sie erhalten Fehler. Wenn Sie es einlegen und das Speicherproblem dadurch nicht behoben wird, nehmen Sie es wieder heraus. Denken Sie daran, im Release-Modus zu testen und Äpfel mit Äpfeln zu vergleichen.
Das einzige Mal, wenn etwas schief gehen kann, ist, wenn man moralisch wird. Es ist kein Werteproblem; Viele Programmierer sind gestorben und direkt in den Himmel gekommen, mit vielen unnötigen GC-Sammlungen in ihrem Code, die sie überleben.
quelle