2010-03-08 19 views
11

Ich habe eine andere aktive Frage HERE in Bezug auf einige hoffnungslose Speicherprobleme, die möglicherweise LOH Fragmentierung unter möglicherweise anderen Unbekannten beinhalten.Große Arrays und LOH-Fragmentierung. Was ist die akzeptierte Konvention?

Was meine Frage ist jetzt, was ist der akzeptierte Weg, Dinge zu tun? Wenn meine App in Visual C# durchgeführt werden muss und große Arrays mit der Intune-Zahl [4000000] behandelt werden müssen, wie kann ich dann die Verweigerung des Garbage Collectors mit dem LOH umgehen?

Es scheint, dass ich gezwungen bin, irgendwelche großen Arrays global zu machen, und niemals das Wort "neu" um irgendeinen von ihnen zu verwenden. Also, ich bin links mit ungeahnten globalen Arrays mit "maxindex" Variablen anstelle von ordentlich großen Arrays, die von Funktionen weitergegeben werden.

Mir wurde immer gesagt, dass dies eine schlechte Übung sei. Welche Alternative gibt es?

Gibt es eine Art Funktion für die Melodie von System.GC.CollectLOH("Seriously")? Gibt es möglicherweise eine Möglichkeit, Garbage Collection auf etwas anderes als System.GC auszulagern?

Wie lauten die allgemein anerkannten Regeln für den Umgang mit großen (> 85Kb) Variablen?

Antwort

22

Erstens sammelt der Müllsammler sammeln Sie die LOH, also seien Sie nicht sofort Angst vor seiner Anwesenheit. Die LOH wird gesammelt, wenn Generation 2 gesammelt wird.

Der Unterschied ist, dass die LOH nicht verdichtet wird, was bedeutet, dass wenn Sie ein Objekt haben, das eine lange Lebensdauer hat, dann teilen Sie die LOH in zwei Abschnitte - den Bereich vor und den Bereich danach Objekt. Wenn dieses Verhalten weiterhin auftritt, kann es passieren, dass der Abstand zwischen langlebigen Objekten für nachfolgende Zuweisungen nicht groß genug ist und .NET mehr und mehr Speicher zuweisen muss, um Ihre großen Objekte, dh die LOH, zu platzieren wird fragmentiert.

Nun, die LOH kann in der Größe schrumpfen, wenn der Bereich an seinem Ende vollständig frei von lebenden Objekten ist, so das einzige Problem ist, wenn Sie Objekte dort für eine lange Zeit (z. B. die Dauer der Anwendung).

Strategien LOH Fragmentierung zu vermeiden sind:

  • Vermeiden Sie große Objekte zu schaffen, um zu hängen. Im Grunde genommen bedeutet dies nur große Arrays oder Objekte, die große Arrays umhüllen (wie der MemoryStream, der ein Byte-Array umschließt), da nichts anderes so groß ist (Komponenten komplexer Objekte werden separat auf dem Heap gespeichert und sind daher selten sehr groß). Achten Sie auch auf große Wörterbücher und Listen, da diese intern ein Array verwenden.
  • Achten Sie auf doppelte Arrays - die Schwelle für diese in die LOH gehen ist viel, viel kleiner - ich kann mich nicht an die genaue Zahl erinnern, aber es sind nur ein paar tausend.
  • Wenn Sie einen MemoryStream benötigen, sollten Sie eine Chunked-Version erstellen, die auf mehrere kleinere Arrays anstatt auf ein großes Array zurückgreift. Sie könnten auch eine benutzerdefinierte Version von IList und IDictionary erstellen, die Chunking verwendet, um zu vermeiden, dass Dinge in der LOH landen.
  • Vermeiden Sie sehr lange Remoting-Aufrufe, da Remoting stark von MemoryStreams Gebrauch macht, die die LOH während der Dauer des Aufrufs fragmentieren können.
  • Achten Sie auf String-Interning - aus irgendeinem Grund werden diese als Seiten auf der LOH gespeichert und kann zu schwerwiegenden Fragmentierung führen, wenn Ihre Anwendung weiterhin neue Strings zu intern, dh die Verwendung von string.Intern, wenn die Menge der Strings bekannt ist sei endlich, und die ganze Menge ist schon früh im Leben der Anwendung anzutreffen. (Siehe my earlier question.)
  • Verwenden Sie Son of Strike, um zu sehen, was genau den LOH-Speicher verwendet. Weitere Informationen hierzu finden Sie unter this question.

Edit: die LOH-Schwelle für Doppel-Arrays scheint 8k zu sein.

+0

Das ist wahrscheinlich eine schlechte Frage ... aber wie bekomme ich CDB und SOS? Google scheint das nicht zu wissen. Alle Links, denen ich folge, um CDB herunterzuladen, verweisen mich auf eine MSDN-Suchergebnisseite, die "DDK3" auflistet, unter anderem, die nicht wie CDB klingen. –

+1

Download 'Debugging Tools für Windows'. CDB ist der Konsolen-Debugger und WinDbg ist das 'grafische' Äquivalent (es ist immer noch Text-Modus, aber in einem MDI-Fenster gerendert). SOS kommt mit .NET: '% systmeroot% \ microsoft.net \ framework \ v2.0.50727 \ sos.dll' (.NET 3 verwendet die .NET 2 SOS.dll, .NET 4 kommt mit einer eigenen Version.) –

+2

Auch , Sie können scheinbar SOS aus dem Direktfenster in Visual Studio laden (Vermeidung von CDB), aber ich habe es noch nie versucht. –

5

Das erste, was einem in den Sinn kommt, ist, das Array in kleinere aufzuteilen, damit sie nicht den Speicher erreichen, der für den GC benötigt wird, um den LOH hineinzusetzen. Sie könnten die Arrays in kleinere von etwa 10.000 spucken und ein Objekt erstellen, das basierend auf dem übergebenen Indexer weiß, in welchem ​​Array gesucht werden soll.

Jetzt habe ich den Code nicht gesehen, aber ich würde auch fragen, warum Sie ein so großes Array benötigen. Ich würde möglicherweise den Refactoring des Codes betrachten, so dass all diese Informationen nicht im Speicher auf einmal gespeichert werden müssen.

+0

Meine Anwendung in Bildern von möglicher Größe 1024x720 nimmt gefunden werden, wandelt diese in eine Matrix von „Höhe“ auf Basis von Pixelintensität. Dann nimmt es diese Matrix und rendert eine Oberflächenkarte in OpenGL. Also, ich brauche wirklich alle diese Daten zur gleichen Zeit. –

5

Sie bekommen es falsch. Sie brauchen keine Array-Größe 4000000 und Sie müssen auf keinen Fall den Garbace Collector aufrufen.

  • Schreiben Sie Ihre eigene IList-Implementierung. Wie "PagedList"
  • Speichern Sie Elemente in Arrays von 65536 Elementen.
  • Erstellen Sie ein Array von Arrays für die Seiten.

Damit können Sie im Grunde alle Ihre Elemente mit nur einer Umleitung zugreifen. Und da die einzelnen Arrays kleiner sind, ist die Fragmentierung kein Problem ...

... wenn es ... ist, dann REUSE Seiten. Werfen Sie sie nicht weg, legen Sie sie auf eine statische "PageList" und ziehen Sie sie zuerst von dort weg. All dies kann innerhalb Ihrer Klasse transparent gemacht werden.

Die wirklich gute Sache ist, dass diese Liste ziemlich dynamisch im Speicherverbrauch ist. Möglicherweise möchten Sie die Größe des Halter-Arrays (Redirector) ändern. Selbst wenn nicht, sind es nur etwa 512 kbdata pro Seite.

Second-Level-Arrays haben im Grunde 64k pro Byte - das ist 8 Byte für eine Klasse (512 KB pro Seite, 256 KB auf 32 Bit) oder 64 KB pro STRB.

Technisch:

Schalten int [] in int [] []

Entscheiden Sie, ob 32 oder 64 Bit besser ist, wie Sie wollen;) Beide ahve Vor- und Nachteile.

Der Umgang mit EINEM großen Array ist in keiner Sprache so einfach - wenn Sie das tun, dann ... im Grunde .... beim Programmstart zuweisen und nie neu erstellen. Einzige Lösung.

+0

Also braucht ein gezacktes Array int [] [] keinen kontinuierlichen Speicher? Verhalten sich ein Array wie int [,] genauso? –

+0

Ich glaube, new int [,] würde einen zusammenhängenden Block zuweisen. Gezackte Arrays werden separat zugewiesen. –

0

Ich füge eine Ausarbeitung zu der obigen Antwort hinzu, in Bezug darauf, wie das Problem entstehen kann.Fragmentierung der LOH hängt nicht nur davon ab, ob die Objekte langlebig sind, aber wenn Sie die Situation haben, dass es mehrere Threads gibt und jede von ihnen große Listen erstellt, die auf die LOH gehen, dann könnten Sie die Situation haben, dass der erste Thread muss seine Liste erweitern, aber das nächste zusammenhängende Speicherbit wird bereits von einer Liste aus einem zweiten Thread belegt, daher weist die Laufzeit dem ersten Thread List neuen Speicher zu und hinterlässt ein ziemlich großes Loch. Das passiert derzeit bei einem Projekt, das ich geerbt habe, und obwohl die LOH ungefähr 4,5 MB beträgt, hat die Laufzeit insgesamt 117 MB freien Speicher, aber das größte freie Speichersegment ist 28 MB. Eine andere Möglichkeit, die ohne mehrere Threads passieren könnte, ist, wenn Sie mehr als eine Liste in irgendeiner Art von Schleife hinzugefügt haben und jeder über den ihm zugewiesenen Speicher hinaus expandiert, dann springt jeder über den anderen, wenn er wächst außerhalb ihrer zugewiesenen Räume.

Ein nützlicher Link lautet: https://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/

immer noch nach einer Lösung für diesen, ist es vielleicht eine Möglichkeit, eine Art von gepoolten Objekten und Anforderung aus dem Pool zu nutzen, wenn die Arbeit zu tun. Wenn Sie mit großen Arrays arbeiten, besteht eine andere Möglichkeit darin, eine benutzerdefinierte Sammlung zu entwickeln, z. eine Sammlung von Sammlungen, so dass Sie nicht nur eine riesige Liste haben, sondern sie in kleinere Listen aufteilen, von denen jede die LOH vermeidet.

5

Es ist eine alte Frage, aber ich finde, es tut nicht weh, die Antworten mit den in .NET eingeführten Änderungen zu aktualisieren. Es ist jetzt möglich, den Large Object Heap zu defragmentieren. Natürlich sollte die erste Wahl sein, um sicherzustellen, dass die besten Design-Entscheidungen getroffen wurden, aber es ist schön, diese Option jetzt zu haben.

https://msdn.microsoft.com/en-us/library/xe0c2357(v=vs.110).aspx

„Beginnend mit dem .NET Framework 4.5.1, können Sie den großen Objektheap (LOH) kompakt durch die GCSettings.LargeObjectHeapCompactionMode Eigenschaft auf GCLargeObjectHeapCompactionMode.CompactOnce Einstellung bevor die Collect-Methode aufrufen, wie die folgenden Beispiel veranschaulicht. "

GCSettings kann in dem Namensraum System.Runtime

GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; 
GC.Collect(); 
Verwandte Themen