2009-08-22 6 views
1

Lassen Sie uns sagen, dass ich den folgenden Code haben ...Loop-Scope und Speicher Ausgabe

StringBuilder sb = new StringBuilder(); 
for (int i = 0; i < 10000; i++) 
{ 
    MyCustomClass myObj = new MyCustomClass(); 
    sb.Append(myObj.RenderShortString()); 
} 
Console.Write(sb.ToString()); 

Und das myCustomClass nehmen ist ein sehr großes Objekt. Nehmen wir zum Beispiel an, dass es ein internes Mitglied erstellt und enthält, das eine 1MB-Zeichenfolge enthält. Die RenderShortString() -Methode rendert einfach eine Zeichenfolge mit etwa 100 Zeichen Länge.

Beachten Sie, dass diese Schleifen 10000 Mal durchlaufen werden.

Ich habe etwas im Grunde so, das System.OutOfMemory Ausnahmen innerhalb der Schleife verursacht.

Meine Frage bezieht sich darauf, wenn der Speicherplatz, der für jede Instanz von myObj zugewiesen wurde, vom Garbage Collector bereinigt wird. Ich glaube nicht, dass ich ein Problem mit dem StringBuilder habe, aber ich könnte mich irren. Ich habe das Gefühl, dass die Instanzen von myObj im Speicher zugewiesen werden, aber nicht zum Aufräumen verfügbar sind, bis die Schleife beendet ist. Ist das richtig? Wenn ja, wie kann ich der Anwendung mitteilen, dass ich mit dieser Instanz fertig bin, sobald ich meine gerenderte Zeichenfolge erhalte?

+0

Wird 'GC.Collect()' in der Schleife explizit aufgerufen, um das Problem zu beheben? Keine gute Idee als dauerhafte Lösung, aber es würde beweisen, dass die vorherigen Objekte zur Sammlung verfügbar sind und das Problem "nur" ist, dass der GC keine Chance hat zu laufen. – stevemegson

Antwort

2

Sie möchten vielleicht sogar einen zweiten Blick darauf werfen, wie String verhält, denn das ist recht Speicher als auch raubend sein kann.

Vielleicht nicht das Root-Problem, wenn Sie sagen, MyCustomClass ist Speicher schwer, aber es könnte dazu beitragen, den Prozess über den Rand zu schieben.

Jedes Mal String der Platz ausgeht, ordnet es einen neuen Puffer zweimal die Größe des ursprünglichen Puffer, kopiert die alten Zeichen und lässt die alte Puffer GC'd bekommen. Es ist möglich, dass Sie nur genug verwenden (nennen Sie es x), so dass 2x größer ist als der Speicher, den Sie zuweisen dürfen. Möglicherweise möchten Sie eine maximale Länge für Ihre Zeichenfolgen ermitteln und sie an den Konstruktor von StringBuilder übergeben, damit Sie sie vorab zuweisen können, und Sie sind der doppelten Reallokation nicht ausgeliefert.

See this topic

+0

Guter Punkt. Dies kann durch Auswahl einer realistischen Anfangskapazität für den StringBuilder gemildert werden. Wenn jeder Anruf eine 100-Zeichen-Zeichenfolge erstellt, wissen Sie, dass Sie eine Kapazität von 1 Million benötigen. Dennoch ist dieser Effekt wahrscheinlich nicht das Problem. Es wird höchstens die doppelte Menge an Speicher verwendet (2MB Puffer + 1MB + 500 kB usw.) – Thorarin

+2

Gültiger Punkt, aber angesichts der Zahlen, die das OP gibt, ist dies definitiv nicht das Hauptproblem. –

+0

Alle Antworten waren hilfreich. Ich werde jedoch den String-Builder in meinem realen Beispiel genauer betrachten. Ich werde auch den Profiler verwenden, wie in einer späteren Antwort vorgeschlagen. Vielen Dank! – Kevin

4

Einfache Antwort: Sie wissen nie. Die .NET-Garbage Collection ist nicht deterministisch. Sie können eine Speicherbereinigung mit der Methode System.GC.Collect erzwingen. Darüber hinaus garantiert GC nur, dass der Speicher für nicht erreichbare Objekte freigegeben wird.

+0

Aber wenn Sie wenig Speicher haben, wird der Garbage Collector nicht "extra run", um zu sehen, ob etwas für den Papierkorb bereit ist? –

+0

Es läuft unter wenig Speicherbedingungen. Wenn es jedoch nicht genügend Speicher freigibt, wird die Laufzeit "OutOfMemoryException" auslösen. –

5

Sie sehen ein "Feature" der Garbage Collection in .net. Die Objekte werden einmal außerhalb des Geltungsbereichs zerstört und jedes myObj ist bei jeder Iteration außerhalb des Gültigkeitsbereichs, ABER Sie wissen nicht wann, da der GC nicht deterministisch ist.

Hier ist ein bisschen dies erklärt: Loops and Garbage Collection

Auch hier ist eine interessante Studie über die GC für .net getan. Es wird vorgeschlagen, möglichst nicht "neu" innerhalb der Schleife zu verwenden.

http://nerds-central.blogspot.com/2008/10/net-garbage-collector-pain.html

2

Jede MyCustomClass Instanz wird für die Sammlung in Betracht, wenn RenderShortString() abgeschlossen ist - oder sogar während die Durchführung dieses Verfahrens, in einigen Fällen.

Die eigentliche Garbage Collection wird nur auftreten, wenn der Garbage Collector sich anfühlt, aber aller Wahrscheinlichkeit nach werden die MyCustomClass Instanzen ziemlich bald von gen0 gesammelt werden.

Beachten Sie, dass die MyCustomClass Objekte selbst nicht groß sind, nur weil sie große Zeichenfolgen referenzieren. Diese großen Zeichenfolgen werden auf der large object heap zugewiesen, die noch Müll gesammelt, aber nicht kompaktiert ist. Wenn Sie feststellen, dass Ihre App ziemlich viel Arbeitsspeicher belegt, kann es durchaus sein, dass Instanzen von MyCustomClass als Garbage Collection erfasst wurden, die Strings jedoch nicht.

Es ist eigentlich ziemlich schwer, ein sehr großes benutzerdefiniertes Objekt in .NET zu erstellen. Die offensichtlichsten Beispiele sind:

  • Lange Strings
  • Arrays mit vielen Elementen
  • Boxed structs, wo die Strukturen selbst großen Fest Puffer enthalten (dies sollte selten sein)

Für den größten Teil Objekte selbst sind ziemlich klein, aber es gibt viele Objekte.

1

Sie sollten einen Profiler ausführen, um zu sehen, welche Objekte wie viel Speicher zuweisen. Der Visual Studio-Profiler gibt Ihnen diese Anzahl und sogar wie viele Objekte Sie in jeder Generation haben.