2013-06-11 14 views
10

ich den folgenden Code geschrieben haben, für einen ausreichenden Speicher zu überprüfen,MemoryFailPoint wirft immer eine InsufficientMemoryException auch wenn Arbeitsspeicher verfügbar ist

while (true) 
{ 
    try 
    { 
     // Check for available memory. 
     memFailPoint = new MemoryFailPoint(250); 

     break; 
    } 
    catch (InsufficientMemoryException ex) 
    { 
     if (memFailPoint != null) 
     { 
      memFailPoint.Dispose(); 
     } 

     Thread.Sleep(waitSecond * 1000); 
    } 
} 

ich die oben in einer Konsolenanwendung auf einem Windows 7 64-Bit-Computer ausgeführt wird.

Es gibt 4 Aufrufe alle 10 Sekunden zu dieser Methode.

Am Anfang funktioniert es gut, aber nach 2-3 Stunden gibt es immer einen InsufficientMemoryException geworfen. Ich überprüfte verfügbaren Speicher und es zeigt mehr als 1 GB.

Ich habe viel versucht, aber ich konnte nicht herausfinden, warum das passiert.

Es folgt der Stack-Trace:

at System.Runtime.MemoryFailPoint..ctor(Int32 sizeInMegabytes) 
at SocketListner.AcceptConnection(IAsyncResult res) in H:\Projects\SocketListner.cs:line 308 

Es gibt keine innere Ausnahme.

+0

Was tun Sie noch, um diesen Fehler zu bekommen? Gibt es einen Stack-Trace, den Sie posten können? –

+0

Ich habe Stack-Trace hinzugefügt, bitte Frage erneut beantworten –

+4

"Ich überprüfte verfügbaren Speicher" wie? Sogar mit 1 GB frei, kann es zu fragmentiert sein, um eine Zuweisung von 250 Meg zuzuordnen. Es müsste 250M zusammenhängender freier Speicherplatz im Speicher vorhanden sein, damit dies erfolgreich ist. – spender

Antwort

16

Sie auf diese Methode richtig funktioniert verlassen können, diese Ausnahme ist sehr wahrscheinlich in einem 32-Bit-Prozess auszulösen, wenn Sie nach 250 Megabyte fragen. Das wird schwierig zu bekommen sein, wenn das Programm eine Weile läuft.

Ein Programm nie stürzt mit OOM ab, weil Sie den gesamten verfügbaren virtuellen Speicheradressraum verbraucht haben. Es stürzt ab, weil im Adressraum kein Loch mehr vorhanden ist, das groß genug für die Zuweisung ist. Ihr Code fordert ein Loch an, das groß genug ist, um 250 Megabyte in einem Zug zuzuordnen. Wenn Sie die Ausnahme nicht erhalten, können Sie sicher sein, dass diese Zuweisung nicht fehlschlägt.

Aber 250 Megabyte ist ziemlich viel, das ist ein wirklich großes Array. Und es ist sehr wahrscheinlich, dass es aufgrund eines Problems, das "Adressraumfragmentierung" genannt wird, fehlschlägt. Mit anderen Worten, ein Programm beginnt typischerweise mit mehreren sehr großen Löchern, die größten etwa 600 Megabyte. Zwischen den Zuordnungen, die zum Speichern von Code und Daten, die von der .NET-Laufzeitumgebung verwendet werden, und nicht verwalteten Windows-DLLs verfügbar sind, stehen Lücken zur Verfügung. Wenn das Programm mehr Speicher zuweist, werden diese Löcher kleiner. Es ist wahrscheinlich, etwas Speicher freizugeben, aber das reproduziert kein großes Loch. Sie erhalten normalerweise zwei Löcher, etwa die Hälfte der Größe des Originals, mit einer Zuteilung irgendwo in der Mitte, die das ursprüngliche große Loch in zwei schneidet.

Dies wird Fragmentierung, ein 32-Bit-Prozess genannt, die viel Speicher und gibt ordnet den virtuellen Speicheradressraum fragmentieren endet so das größte Loch, das noch verfügbar ist nach einer Weile, kleiner wird rund 90 Megabyte recht ist typisch.Die Anfrage nach 250 Megabyte wird fast garantiert fehlschlagen. Sie müssen niedriger zielen.

Sie haben zweifellos erwartet, dass es anders funktioniert, so dass die Summe von Zuweisungen bis zu 250 Megabyte funktioniert garantiert. Dies ist jedoch nicht, wie MemoryFailPoint funktioniert, es überprüft nur die größte mögliche Zuordnung. Unnötig zu sagen, vielleicht macht dies es weniger als nützlich. Ansonsten sympathisiere ich mit den .NET-Framework-Programmierern, da es so funktioniert, wie wir es gerne hätten, es ist teuer und kann keine Garantie geben, da die Größe einer Zuweisung am wichtigsten ist.

Virtueller Speicher ist eine reichliche Ressource, die unglaublich billig ist. Aber alles zu konsumieren ist sehr mühsam. Sobald Sie ein Gigabyte davon konsumiert haben, wird das zufällige OOM wahrscheinlicher. Vergessen Sie nicht die einfache Lösung für dieses Problem, Sie laufen auf einem 64-Bit-Betriebssystem. Wenn Sie also nur das Ziel der EXE-Plattform auf AnyCPU ändern, werden Sie im virtuellen Adressraum blödeln. Abhängig von der OS-Edition ist ein Terabyte möglich. Es zerbricht immer noch, aber es interessiert dich einfach nicht mehr, die Löcher sind riesig.

Last but not least, in den Kommentaren sichtbar, hat dieses Problem nichts mit RAM zu tun. Virtueller Speicher ist nicht verwandt mit der Menge an RAM, die Sie haben. Es ist die Aufgabe des Betriebssystems, virtuelle Speicheradressen physikalischen Adressen im RAM zuzuordnen, und zwar dynamisch. Zugriff auf einen Speicherort kann einen Seitenfehler auslösen, das Betriebssystem wird RAM für die Seite reservieren. Und umgekehrt passiert es, dass das Betriebssystem den RAM-Speicher für eine Seite auflöst, wenn sie anderweitig benötigt wird. Sie können niemals den Arbeitsspeicher verlassen, die Maschine wird langsamer, bevor das passieren kann. Das VMMap-Dienstprogramm von SysInternals ist gut zu sehen, wie der virtuelle Adressraum Ihres Programms aussieht, obwohl Sie dazu neigen, in den Informationen für einen großen Prozess zu ertrinken.

+0

Vielen Dank für Ihre detaillierte Analyse und Suggestionen. Ich habe die Größe auf 50MB verringert und die App getestet, aber ich habe Build in 64Bit gemacht, so dass OOM-Ausnahme nicht auftreten sollte, da es früher abgefangen wurde. –

+0

Ich muss sagen, nur willkürlich von 250 auf 50 zu schneiden ist eher zufällig.Es ist wichtig, dass die von Ihnen gewählte Zahl eine Wahrheit hat und die Menge an Speicher darstellt, die Sie benötigen. –

+0

wie das wissen ?, sobald ein Paket verarbeitet wird Speicher sollte frei sein, aber für nach langer Zeit Datenbank wird Engpass und Daten bleiben im Speicher bis zum Timeout, d. H. 30 Sekunden. –

0

Betrachten Sie die GC.GetTotalMemory Methode unter Verwendung der Menge des verfügbaren Speichers vor und nach dem Aufruf zu bestimmen:

memFailPoint = new MemoryFailPoint(250); 

InsufficientMemoryException eine Operation vor dem Start durch den MemoryFailPoint Konstruktor geworfen wird, wenn Sie ein projiziertes Speicherzuordnung größer als die angeben Menge des momentan verfügbaren Speichers. Wie user7116 commented, deshalb sollten Sie zuerst überprüfen.

Das Beispiel in diesem Link sollten Sie eine Lösung geben: MemoryFailPoint Class

Sie können auch diese Msdn Blog-Artikel überprüfen: Out of memory? Easy ways to increase the memory available to your program

+0

Vielen Dank jszigeti :) – terrybozzio

0

MemoryFailPoint Kontrollen für aufeinanderfolgende verfügbare Speicher wie hier dokumentiert: http://msdn.microsoft.com/fr-fr/library/system.runtime.memoryfailpoint.aspx

Sie können sehr wenig Speicher verbrauchen, haben aber viel und dann fragmentiert werden jetzt nicht in der Lage einen konsekutiven Speicherblock der benötigten Größe zuzuordnen. Es ist sehr typisch für dieses Problem, nach ein paar Stunden aufzutreten. Um dies zu vermeiden, verwenden Sie einen Pool von Objekten für Objekte, die Sie instanziieren, wodurch der verwendete Speicherplatz steifer wird.

Verwandte Themen