2009-07-09 9 views
16

Ich habe ein Problem, wo ein paar 3-dimensionale Arrays, die eine große Menge an Speicher zuweisen und das Programm muss sie manchmal mit größer/kleiner ersetzen und wirft einen OutOfMemoryException.Force-Garbage-Collection von Arrays, C#

Beispiel: Es gibt 5 zugewiesene 96MB-Arrays (200x200x200, 12 Byte Daten in jedem Eintrag) und das Programm muss sie durch 210x210x210 (111MB) ersetzen. Es tut es auf ähnliche Weise wie folgt:

Wo Array1-Array5 sind die gleichen Felder zuvor verwendet. Dies sollte die alten Arrays als Kandidaten für die Garbage Collection setzen, aber anscheinend agiert der GC nicht schnell genug und lässt die alten Arrays zu, bevor die neuen zugewiesen werden - was die OOM verursacht - während sie vor den neuen Allokationen freigegeben werden sollten genug.

Was ich suche ist eine Möglichkeit, so etwas zu tun:

GC.Collect(array1) // this would set the reference to null and free the memory 
array1 = new Vector3[210,210,210]; 

Ich bin nicht sicher, ob eine vollständige Garbage collecion eine gute Idee, da dieser Code wäre kann (in manchen Situationen) müssen ziemlich oft ausgeführt werden.

Gibt es einen geeigneten Weg, dies zu tun?

+2

Es scheint wirklich seltsam, dass dies eine OOM-Ausnahme verursachen würde. Haben Sie einen Beispielcode, der das Problem veranschaulicht? – aquinas

+0

Dies ist Teil eines ziemlich großen (noch unveröffentlichten) Programms und leider kann ich nur kleine Ausschnitte des eigentlichen Codes posten, nichts groß genug, um kompilierbar zu sein. –

+0

Ihr Problem besteht nicht in der Speicherverwaltung, sondern darin, dass Sie zu große Arrays verwenden. Warum glauben Sie, dass Sie diese großen Arrays in erster Linie brauchen? Wofür verwenden Sie sie? – Guffa

Antwort

14

Diese auf die ursprüngliche Frage keine genaue Antwort ist, „wie GC‘ zu zwingen, doch halte ich es für Sie Ihre Frage erneut zu prüfen, helfen.

Nachdem Sie Ihren Kommentar zu sehen,

  • Setzen des GC.Sammeln(); scheint zu helfen, alought es immer noch nicht das Problem vollständig zu lösen - aus irgendeinem Grund stürzt das Programm immer noch, wenn etwa 1,3GB zugewiesen sind (Ich benutze System.GC.GetTotalMemory (false); um den tatsächlichen Betrag zu finden).

Ich vermute, dass Sie Speicherfragmentierung haben. Wenn das Objekt groß ist (85000 Byte unter .net 2.0 CLR, wenn ich mich richtig erinnere, ich weiß nicht, ob es geändert wurde oder nicht), wird das Objekt in einem speziellen Heap, Large Object Heap (LOH) zugeordnet. GC reklamiert den Speicher, der von nicht erreichbaren Objekten in LOH verwendet wird, aber führt keine Komprimierung, in LOH, wie es zu anderen Heaps (Gen0, Gen1 und Gen2) aufgrund der Leistung.

Wenn Sie häufig große Objekte zuweisen und freigeben, wird LOH fragmentiert, und obwohl Sie insgesamt mehr freien Speicher haben als Sie benötigen, haben Sie möglicherweise keinen zusammenhängenden Speicherplatz mehr und erhalten daher die OutOfMemory-Ausnahme .

Ich kann in diesem Moment zwei Problemumgehungen denken.

  1. Umzug in 64-Bit-Maschine/OS und nutzen Sie es :) (am einfachsten, aber möglicherweise auch am schwersten je nach knappen Ressourcen)
  2. Wenn Sie nicht # 1 tun können, dann versuchen Sie eine zuzuteilen Zuerst müssen Sie eine große Menge von Arbeitsspeicher verwenden und sie verwenden (es kann erforderlich sein, eine Hilfsklasse zu schreiben, um ein kleineres Array zu manipulieren, das sich tatsächlich in einem größeren Array befindet), um eine Fragmentierung zu vermeiden. Dies kann ein wenig helfen, aber es kann das Problem nicht vollständig lösen, und Sie müssen möglicherweise mit der Komplexität umgehen.
+2

Oder wechseln Sie zu einem gezackten Array - Vector3 [210] [210] [210], die die Zuweisungen aufteilen würde und nicht einen großen Speicherblock verwenden – thecoop

+0

Scheint, dass das Problem tatsächlich durch dieses Fragmentierungsproblem verursacht wird. Leider kann ich das Programm nicht auf 64 Bit portieren, weil XNA nur 32 Bit unterstützt. Die Zuweisung eines großen Arrays beim Start würde den zusätzlichen Speicher auf den kleinen Gehäusen (unter 100x100x100) und die zusätzliche Arbeit wahrscheinlich nicht wert sein, damit es gut funktioniert. Ich habe die Mathematik und selbst wenn ich die volle 2GB verwenden könnte die Verbesserung der maximalen Volume-Größe wäre nicht groß genug (von etwa 200^3 bis 300^3) durch die Schwierigkeiten zu finden/zu implementieren einen Hack zu machen es funktioniert, zumindest nicht für jetzt. Danke an alle, die versucht haben zu helfen! –

7

Das Erzwingen einer Garbage Collection ist nicht immer eine gute Idee (sie kann die Lebenszeit von Objekten in einigen Situationen sogar fördern). Wenn Sie müssen, würden Sie verwenden:

array1 = null; 
GC.Collect(); 
array1 = new Vector3[210,210,210]; 
+2

Nein, Sie sollten GC.Collect überhaupt nicht aufrufen. Die beste Lösung ist, den Verweis einfach zu entfernen, bevor das neue Array zugewiesen wird, und den Garbage Collector arbeiten zu lassen, ohne ihn zu stören. – Guffa

+5

Wenn die Laufzeitumgebung nicht genügend Speicher für die Zuweisung findet, wird eine Sammlung ausgelöst, sodass der explizite Aufruf von GC.Collect nicht erforderlich ist. –

+3

John Doe fragte nach * zwingen * Garbage Collection immerhin. – icelava

1

Sie können nicht gesammelt werden, weil sie irgendwo referenziert werden, die Sie nicht erwarten.

Versuchen Sie als Test, Ihre Referenzen stattdessen auf WeakReferences zu ändern und sehen Sie, ob das Ihr OOM-Problem löst. Wenn nicht, dann verweisen Sie sie woanders hin.

+0

Sie werden nur in dem Code verwendet, der sie generiert, und in dem Code, der die Daten verwendet (XNA VertexBuffer, der zum Rendern eines Volumes verwendet wird), sodass ich denke, dass nichts anderes auf sie verweist. Ich habe noch nie WeakReferences verwendet, aber von diesem Artikel bekomme ich die Chance, dass sie während des Rests der Ausführung freigeschaltet werden - und das würde ein ernstes Problem für den Rest des Codes schaffen. –

2

Wenn ich Ihnen Problem zu spekulieren, ist nicht wirklich, dass Sie von Vector3 gehen [200200200] zu einem Vector3 [210210210] aber, dass die meisten wahrscheinlich, dass Sie haben Ähnliche vorherige Schritte vor diesem:

 
i.e. 
    // first you have 
    Vector3[10,10,10]; 
    // then 
    Vector3[20,20,20]; 
    // then maybe 
    Vector3[30,30,30]; 
    // .. and so on .. 
    // ... 
    // then 
    Vector3[200,200,200]; 
    // and eventually you try 
    Vector3[210,210,210] // and you get an OutOfMemoryException.. 

Wenn das stimmt, ich würde eine bessere Allokationsstrategie vorschlagen. Probieren Sie die Zuweisung aus - vielleicht verdoppelt sich die Größe jedes Mal, anstatt immer nur den benötigten Speicherplatz zuzuweisen. Vor allem, wenn diese Arrays sind jemals von Objekten verwendet, die die Puffer an Pin müssen (dh, wenn die Bindung an nativen Code haben)

Also, statt der oben genannten, haben so etwas wie dieses:

// first start with an arbitrary size 
Vector3[64,64,64]; 
// then double that 
Vector3[128,128,128]; 
// and then.. so in thee steps you go to where otherwise 
// it would have taken you 20.. 
Vector3[256,256,256]; 
+0

Beachten Sie, dass einige der integrierten Auflistungsklassen dies für Sie übernehmen. – Brian

+0

Ich stimme zu, ständig Neuzuordnung Arrays wie das ist nur betteln für OOM-Fehler. –

+0

Tatsächlich wird die "Größenanpassung" der Arrays durch Benutzereingaben ausgelöst und ist nicht vorhersehbar (obwohl ich sie manuell in Schritten zum Testen erhöhen würde). Overlaceocating ist keine Option, das würde es früher zum Absturz bringen, weil die Gesamtgröße kubisch zunimmt. –

0

Ein OutOfMemory Ausnahme löst intern einen GC-Zyklus automatisch einmal aus und versucht die Zuweisung erneut, bevor die Ausnahme tatsächlich auf Ihren Code angewendet wird. Die einzige Möglichkeit OutOfMemory-Ausnahmen zu haben ist, wenn Sie Referenzen auf zu viel Speicher haben. Löschen Sie die Referenzen so schnell wie möglich, indem Sie sie null zuweisen.

4

Ist das nicht nur große Objekt Heap Fragmentierung? Objekte> 85.000 Bytes werden auf dem großen Objekt-Heap zugeordnet. Der GC gibt in diesem Heap Speicherplatz frei, komprimiert jedoch die verbleibenden Objekte nie. Dies kann zu einem unzureichenden zusammenhängenden Speicher führen, um ein großes Objekt erfolgreich zuzuweisen.

Alan.

0

Ein Teil des Problems kann sein, dass Sie ein mehrdimensionales Array zuweisen, das als einzelner zusammenhängender Speicherblock auf dem großen Objekt-Heap dargestellt wird (weitere Details here). Dies kann andere Zuweisungen blockieren, da es keinen freien zusammenhängenden Block zu verwenden gibt, selbst wenn irgendwo noch freier Speicherplatz vorhanden ist, daher der OOM.

Versuchen Sie es als gezackten Array Zuweisung - Vector3 [210] [210] [210] -, die die Arrays um Speicher verbreitet und nicht als ein einzelner Block, und sehen, ob das Angelegenheiten verbessert

1

Ich verstehe, was Sie "Versuchen zu tun und drängen auf sofortige Garbage Collection ist wahrscheinlich nicht der richtige Ansatz (da der GC subtil in seinen Wegen und schnell zu Wut ist).

Das sagte, wenn Sie wollen diese Funktionalität, warum nicht erstellen?

public static void Collect(ref object o) 
{ 
    o = null; 
    GC.Collect(); 
} 
+0

Das macht nichts. Sie erstellen eine Kopie des Verweises auf ein Objekt o zeigen auf Null, was nichts mit dem ursprünglichen Objekt zu tun hat o. –

+0

Sie verstehen, dass wenn ich eine Ref-Variable side-Effekt, ändert es auch die Kopie des Aufrufers, richtig? – plinth

+0

Haben Sie +1 für mich, wird dieser Code tatsächlich die ursprüngliche Variable auf Null setzen, da sie den Zeiger der Originalvariablen durch Referenz verändert. –

0

John, Erstellen von Objekten> 85000 Bytes wird das Objekt in den großen Objekt-Heap landen. Der große Objektspeicher wird niemals komprimiert, stattdessen wird der freie Speicherplatz erneut verwendet. Dies bedeutet, dass Sie, wenn Sie jedes Mal größere Arrays zuweisen, in Situationen landen können, in denen LOH fragmentiert ist, daher das OOM.

Sie können überprüfen, ob dies der Fall ist, indem Sie mit dem Debugger an der Stelle von OOM brechen und einen Dump erhalten, diesen Dump an MS durch einen Verbindungsfehler (http://connect.microsoft.com) zu senden wäre ein guter Anfang.

Was ich Ihnen versichern kann, ist, dass der GC das Richtige tun wird, um Ihre Allokationsanforderung zu erfüllen. Dazu gehört das Starten eines GC, um den alten Müll zu säubern, um die neuen Zuteilungsanforderungen zu erfüllen.

Ich weiß nicht, was ist die Politik der Freigabe von Speicherauszügen auf Stackoverflow, aber ich würde mich freuen, einen Blick auf Ihr Problem mehr zu verstehen.