Was ist so falsch an der Verwendung von GC.Collect ()?

103

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.

Falle
quelle
24
"Die geringste Änderung im Programmablauf könnte katastrophale Folgen haben ... jemand könnte sterben" - sind Sie sicher, dass C # .NET für Ihre Zwecke ausreichend deterministisch ist?
Steve Jessop
4
Weder Windows noch .NET sind Echtzeitplattformen, und daher können Sie keine Leistungsmetriken garantieren, zumindest nicht genug, um Menschenleben zu riskieren. Ich stimme jedem zu, dass Sie entweder übertreiben oder nachlässig sind.
Sergio Acosta
3
LOL bei "einem dieser Dinge, die seriöse Programmierer niemals benutzen würden, selbst diejenigen, die nicht einmal wissen, wofür es ist"! Programmierer, die Dinge verwenden, die nicht wissen, warum, sind in meinem Buch kaum die angesehensten. :)
The Dag

Antworten:

87

Aus Ricos Blog ...

Regel 1

Tu es nicht.

Dies ist wirklich die wichtigste Regel. Es ist fair zu sagen, dass die meisten Verwendungen von GC.Collect () eine schlechte Idee sind, und ich habe in der ursprünglichen Veröffentlichung ausführlich darauf eingegangen, sodass ich das hier nicht wiederholen werde. Fahren wir also fort mit ...

Regel Nr. 2

Ziehen Sie in Betracht, GC.Collect () aufzurufen, wenn gerade ein einmaliges Ereignis aufgetreten ist und dieses Ereignis höchstwahrscheinlich dazu geführt hat, dass viele alte Objekte gestorben sind.

Ein klassisches Beispiel hierfür ist, wenn Sie eine Clientanwendung schreiben und ein sehr großes und kompliziertes Formular anzeigen, mit dem viele Daten verknüpft sind. Ihr Benutzer hat gerade mit diesem Formular interagiert und möglicherweise einige große Objekte erstellt ... beispielsweise XML-Dokumente oder ein oder zwei große DataSets. Wenn das Formular geschlossen wird, sind diese Objekte tot und GC.Collect () stellt den ihnen zugeordneten Speicher wieder her ...

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.

Regel Nr. 1 sollte Regel Nr. 2 ohne starke Beweise übertreffen.

Messen, messen, messen.

Jon Norton
quelle
9
Ich würde sagen, das ist nur das Alte. Nichts ist wirklich schlecht oder gefährlich, wenn Sie wissen, was Sie tun und daher wissen, wann und wie es zu tun ist und welche Nebenwirkungen es hat. Dinge wie nicht nie, nie xxxx verwenden werden dort platziert, um die Welt vor miesen Programmierern zu schützen: D
Jorge Córdoba
siehe auch stackoverflow.com/questions/233596/…
Ian Ringrose
Ich sage nicht, dass die Verwendung von GC.Collect eine gute Praxis ist. Aber manchmal ist es eine schnelle Möglichkeit, Probleme zu lösen, ohne die wahre Ursache zu kennen. Es ist hässlich, ich weiß, aber es funktioniert, und es scheint mir kein schlechter Ansatz zu sein, besonders wenn nicht viel Zeit bleibt, um die Grundursache des Problems herauszufinden, und Ihr Chef hinter Ihnen steht ... wissen Sie.
Silent Sojourner
58

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.

Aaron Fischer
quelle
3
Das ist sehr wahr, aber ich weiß nicht, ob sie Annahmen treffen könnten, die für alle Entwicklungen gelten.
MasterMastic
2
@ Ken Nein, sie können nicht. Aber sind Sie dazu in einer besseren Position? Oder werden Sie Code schreiben, der bestimmte Hardware, eine bestimmte Betriebssystemversion usw. voraussetzt? Das Schmerz / Gewinn-Verhältnis ist bei diesem zu hoch.
Der Tag
2
@ TheDag IMO natürlich bin ich. Wenn ich Speicher freigebe und so weiter, kümmert mich die Hardware nicht wirklich, weil das der Job des Betriebssystems ist, um damit umzugehen. Das Betriebssystem interessiert mich auch nicht, da ich eine Schnittstelle habe, die allen gemeinsam ist, für die ich programmiere. (zB ist es mir egal, ob es Windows, Mac oder Linux ist: Wenn ich Speicher in C / C ++ zuordne / freigebe, ist es neu / malloc / dealloc löschen). Ich könnte mich immer irren, also zögern Sie nicht, mich zu korrigieren.
MasterMastic
@MasterMastic hat mallocnur 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. Wenn malloc"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.).
Luaan
24

Wie wäre es also, wenn Sie COM-Objekte wie MS Word oder MS Excel aus .NET verwenden? Ohne Aufruf GC.Collectnach dem Freigeben der COM-Objekte haben wir festgestellt, dass die Word- oder Excel-Anwendungsinstanzen noch vorhanden sind.

Tatsächlich verwenden wir folgenden Code:

Utils.ReleaseCOMObject(objExcel)

' Call the Garbage Collector twice. The GC needs to be called twice in order to get the
' Finalizers called - the first time in, it simply makes a list of what is to be finalized,
' the second time in, it actually does the finalizing. Only then will the object do its 
' automatic ReleaseComObject. Note: Calling the GC is a time-consuming process, 
' but one that may be necessary when automating Excel because it is the only way to 
' release all the Excel COM objects referenced indirectly.
' Ref: http://www.informit.com/articles/article.aspx?p=1346865&seqNum=5
' Ref: http://support.microsoft.com/default.aspx?scid=KB;EN-US;q317109
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()

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‚s CollectMethode selbst Public?

Dib
quelle
3
Dies würde eine großartige neue StackOverflow-Frage aufwerfen, dh: Wie COM-Instanzen gelöscht werden können, ohne den GC aufzurufen. Unter besonderer Berücksichtigung nicht verwalteter Zirkelverweise. Dies ist eine der Herausforderungen, die mich bei der Aktualisierung meines VB6 Outlook-Add-Ins auf C # vorsichtig gemacht haben. (Wir haben viel Arbeit geleistet, um Codierungsmuster und Testfälle auf der VB-Seite zu entwickeln, die garantierten, dass COM-Referenzen deterministisch getötet wurden, wenn sie nicht mehr benötigt wurden.)
Rkagerer
2
Wenn dies allgemein für COM-Objekte gilt, ist dies möglicherweise ein gültiges Szenario. Aber auf Anhieb würde ich sagen, dass das Problem wahrscheinlich darin besteht, dass Sie eine Client-Anwendung verwenden, die für einen interaktiven Desktop als COM-Server entwickelt wurde. Aus der MSDN-Wissensdatenbank: "Microsoft empfiehlt und unterstützt derzeit keine Automatisierung von Microsoft Office-Anwendungen aus unbeaufsichtigten, nicht interaktiven Clientanwendungen oder -komponenten (einschließlich ASP, ASP.NET, DCOM und NT Services), da Office kann instabiles Verhalten und / oder Deadlock aufweisen, wenn Office in dieser Umgebung ausgeführt wird. "
Der
2
@TheDag - Microsoft empfiehlt dies möglicherweise nicht, aber viele von uns mussten alten VB6-Code mit Office-Interop auf .NET Windows-Apps portieren. Ich habe monatelang gearbeitet, bis ich endlich alle unsichtbaren hängenden Referenzen für ein großes Konvertierungsprojekt von VB6 zu .Net losgeworden bin. Das Erlernen der Freigabe in umgekehrter Zuordnungsreihenfolge und das Halten lokaler Verweise auf JEDES einzelne COM-Objekt einschließlich der Sammlungen hat jedoch geholfen.
Dib
15

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 Collectsei 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.CollectAussagen 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 die IDisposableOn-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.

Jason Short
quelle
Der Schuldige sind oft Ereignisse. Immer wenn eine Instanzmethode als Ereignishandler verwendet wird, hat der Herausgeber des Ereignisses über den Ereignisdelegierten einen Verweis auf den Abonnenten. Die einzige "einfache" Möglichkeit, Probleme zu vermeiden, besteht darin, nur Publisher zu verwenden, die höchstens so langlebig sind wie die Abonnenten (z. B. ist eine TextBox, die ein Ereignis veröffentlicht, das von dem enthaltenen Formular behandelt wird, kein Problem, da das Textfeld nicht angenommen wird außerhalb der Form leben). Beispiel für ein Problemszenario: Singleton-Modell, temporäre Ansichten, die Modellereignisse verarbeiten.
Der Tag
5
Wie kann man die gesamte Maschine vermasseln?
Adam R. Gray
13

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.

rjohnston
quelle
12

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.

TheZenker
quelle
10

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.

TraumaPony
quelle
9

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.

wnoise
quelle
Wahr. Aber automatisierte Tests, die die Fehlerbedingung "Objekt, das nicht für die Speicherbereinigung geeignet ist, aber sein sollte" abfangen können, wären wertvoll. Dies könnte über eine Kombination aus Factory-Logik, Destruktor-Logik und GC.Collect erreicht werden. Beispielsweise verfügt Ihre Entity-Klasse über eine IObjectTracker-Eigenschaft, die normalerweise null ist, aber von der Test-Entity-Factory zugewiesen wird. Die Fabrik benachrichtigt den Verfolger auch über die Geburt eines Objekts, während der Zerstörer ihn (falls vorhanden) über den Tod informiert. Wenn Sie wissen, dass "der Destruktor für alle Speicherbereinigungsobjekte ausgeführt wurde", können Sie den Tracker-Status überprüfen, um Lecks zu erkennen.
Der Tag
7

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.

Ta01
quelle
2
Sie verursachen nicht nur den Stapelspaziergang, sondern der Hauptthread Ihrer Anwendung (und alle von ihm erstellten untergeordneten Threads) werden eingefroren, sodass der GC den Stapel durchlaufen kann . Je mehr Zeit Ihre App in GC verbringt, desto mehr Zeit verbringt sie eingefroren.
Scott Dorman
3
Ich mache mir mehr Sorgen über einen App-Absturz aufgrund einer Ausnahme wegen Speichermangels als über eine langsame Leistung, da die App / GC Dinge wegwirft, die nicht mehr benötigt werden. Weiß jemand, warum Microsoft anscheinend eine OOM-Ausnahme auslöst, ohne zuerst den Müll wegzuwerfen? (Ohne diesen offensichtlichen Schritt - oder zumindest eine Erklärung, warum dieser Schritt nicht versucht zu werden scheint, bevor eine OOM-Ausnahme ausgelöst wird. Ich bin mir nicht sicher, ob ich daran glaube, dass Dinge "automatisch" so geschehen, wie sie sollen. "
Wonderbird
6

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:

var obj = /* object utilizing the memory, in my case Form itself */
GC.Collect(GC.GetGeneration(obj ,GCCollectionMode.Optimized).
Venugopal M.
quelle
5

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 a WeakReference, 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.Collectden 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 wie GC.Collectnicht aufgerufen, aber das System hat etwas Müll angesammelt, was dazu führte, dass die nachfolgende Sammlung früher als GC.Collectbisher benötigt wurde wurde gerufen.

Die Zeiten, in denen ich sehen kann, dass sie GC.Collectwirklich 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 ein GC.CollectAufruf, 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.

Superkatze
quelle
5

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.

Tal
quelle
2
Das Aufrufen Disposesoll keinen verwalteten Speicher freigeben. Sie scheinen nicht zu wissen, wie das Speichermodell in .NET funktioniert.
Andrew Barber
4

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 ...

For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
End Sub

Durch Verschieben des GC-Aufrufs außerhalb des Bereichs der Unterroutine wurde der Müll gesammelt und der Speicher freigegeben.

For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
End Sub

Ich hoffe, dies hilft anderen, die mit der .NET-Speicherbereinigung frustriert sind, wenn sie die Aufrufe von zu ignorieren scheint GC.Collect().

Paul Smith

Paul Smith
quelle
4

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.

Gerard ONeill
quelle
2

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.

Sergio Acosta
quelle
2
Interessant, aber die Dokumentation besagt, dass AddMemoryPressure verwendet werden sollte, wenn "ein kleines verwaltetes Objekt eine große Menge nicht verwalteten Speichers zuweist". (Hervorhebung von mir)
Robert Paulson
2

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.

rauben
quelle
1
Die heuristische Natur des Müllsammlers und die Tatsache, dass sie diese Funktionalität der Außenwelt ausgesetzt haben, lassen mich sie als etwas betrachten, das nützlich ist, wenn es dort eingesetzt wird, wo es sein muss. Das Problem besteht nicht darin, es zu verwenden, sondern zu wissen, wie, wo und wann es verwendet werden soll.
Falle
Ganz zu schweigen von den besseren Kenntnissen der GCs über jede andere Anwendung und deren Speicherbedarf. Der GC handelt den Speicher mit dem Betriebssystem aus und wird daher vom verfügbaren physischen Speicher und allen anderen Prozessen auf dem Computer beeinflusst, die sowohl verwaltet als auch nicht verwaltet werden. Obwohl ich bezweifle, dass der GC wirklich weiß, "wann ein guter Zeitpunkt zum Sammeln ist", ist es sehr wahrscheinlich, dass er insgesamt eine bessere Strategie hat als ... JEDE einzelne Anwendung. ;)
The Dag
2

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.

Sam
quelle
5
das ist vielleicht eine Verallgemeinerung
MickyD
1

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.

Christopher Elliott
quelle
1

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.

Scott Dorman
quelle
Er könnte das .Net Micro Framework verwenden, das für Echtzeitumgebungen entwickelt wurde.
TraumaPony
@TraumaPony: Überprüfen Sie das Diagramm unten auf dieser Seite. Msdn.microsoft.com/en-us/embedded/bb278106.aspx : Das Micro Framework wurde eindeutig nicht für Echtzeitumgebungen entwickelt. Es wurde jedoch für eingebettete Umgebungen (wie WinCE) mit geringerem Strombedarf entwickelt.
Scott Dorman
1

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.

FastAl
quelle