2013-02-22 11 views
12

Ich hatte einige Probleme mit einem WCF-Webdienst (einige Speicherauszüge, Speicherlecks usw.) und ich führe ein Profiling-Tool (ANTS Memory Profiles).Speicher aus C# -Wörterbuch in einem statischen Objekt freigeben

Nur um herauszufinden, dass sogar mit der Verarbeitung über (ich führe einen bestimmten Test und dann gestoppt), Generation 2 ist 25% des Speichers für den Web-Service. Ich habe diesen Speicher aufgespürt, um festzustellen, dass ich ein Wörterbuchobjekt mit (-1, null) Elementen mit -1 Hashcode hatte.

Der Workflow des Web-Service bedeutet, dass während der Verarbeitung bestimmte Elemente hinzugefügt und dann aus dem Wörterbuch entfernt werden (einfach Add und Remove). Keine große Sache. Aber nachdem alle Elemente entfernt wurden, scheint das Wörterbuch voll von (null, null) KeyValuePair s zu sein. Tausende von ihnen in der Tat, so dass sie einen großen Teil des Speichers belegen und schließlich einen Überlauf auftritt, mit der entsprechenden erzwungenen Anwendung Pool Recycling und DW20.exe erhalten alle CPU-Zyklen, die es bekommen kann.

Das Wörterbuch ist in der Tat Dictionary<SomeKeyType, IEnumerable<KeyValuePair<SomeOtherKeyType, SomeCustomType>>> (System.OutOfMemoryException because of Large Dictionary), also habe ich bereits überprüft, ob es eine Art von Bezug hält Dinge.

Das Wörterbuch ist in einem statischen Objekt enthalten (um es für verschiedene Verarbeitungsthreads durch Verarbeitung zugänglich zu machen) also von dieser Frage und vielem mehr (Do static members ever get garbage collected?) Ich verstehe, warum dieses Wörterbuch in Generation 2 ist. Aber das ist auch die Ursache von denen (null, null)? Auch wenn ich Gegenstände aus dem Wörterbuch entferne, ist immer etwas im Speicher belegt?

Es ist kein Geschwindigkeitsproblem wie in dieser Frage Deallocate memory from large data structures in C#. Es scheint, dass Speicher nie zurückgewonnen wird.

Gibt es etwas, was ich tun kann, um tatsächlich Elemente aus dem Wörterbuch zu entfernen, nicht einfach nur mit (Null, Null) Paaren zu füllen? Gibt es noch etwas, das ich überprüfen muss?

+0

+ !: Für die richtige Forschung und eine gute Frage zu schreiben. – Virtlink

+1

'List <>' hat eine 'TrimExcess()' Methode, 'Dictionary <,>' leider nicht. Dachte, das war ein einfaches Ergebnis :) –

+0

Macht das Aufrufen von Dictionary.Clear() einen Unterschied? – Alex

Antwort

9

Wörterbücher speichern Elemente in einer Hashtabelle. Ein Array wird intern dafür verwendet. Aufgrund der Funktionsweise von Hash-Tabellen muss dieses Array immer größer sein als die tatsächliche Anzahl der gespeicherten Elemente (mindestens ca. 30% größer). Microsoft verwendet einen Ladefaktor von 72%, d. H. Mindestens 28% des Arrays sind leer (siehe An Extensive Examination of Data Structures Using C# 2.0 und speziell The System.Collections.Hashtable Class und The System.Collections.Generic.Dictionary Class). Daher können die Null/Null-Einträge nur diesen freien Speicherplatz darstellen.

Wenn das Array zu klein ist, wird es automatisch vergrößert; Wenn Elemente jedoch entfernt werden, wird das Array nicht verkleinert, aber der freizugebende Speicherplatz sollte wiederverwendet werden, wenn neue Elemente eingefügt werden.

Wenn Sie die Kontrolle über dieses Wörterbuch, könnten Sie versuchen es, um neu zu erstellen, es zu schrumpfen:

theDict = new Dictionary<TKey, IEnumerable<KeyValuePair<TKey2, TVal>>>(theDict); 

Aber das Problem von den tatsächlichen (nicht leer) Einträgen entstehen könnte. Ihr Wörterbuch ist statisch und wird daher niemals automatisch vom Garbage Collector zurückerlangt, es sei denn, Sie weisen ihm ein anderes Wörterbuch oder null (theDict = new ... oder theDict = null) zu.Dies gilt nur für das Wörterbuch selbst, das statisch ist, nicht für seine Einträge. Solange Verweise auf entfernte Einträge an anderer Stelle vorhanden sind, bleiben sie bestehen. Der GC wird jedes Objekt (früher oder später) zurückfordern, auf das durch eine Referenz nicht mehr zugegriffen werden kann. Es spielt keine Rolle, ob dieses Objekt als statisch deklariert wurde oder nicht. Die Objekte selbst sind nicht statisch, nur ihre Referenzen.

+0

Danke für die Information, die Dinge werden für mich klarer. Obwohl ich die Vorteile von verfügbarem freien Speicher sehe, möchte ich es dennoch so "verkleinern", wie Sie und @usr es vorgeschlagen haben. Ich muss nur besser analysieren, wenn ich es will. –

+0

@Olivier Wenn man sagt, dass es nicht schrumpft, wenn Gegenstände entfernt werden, bedeutet das, dass der Speicher noch benutzt wird? –

+0

@Siraj Mansour: Ja. –

4

Sieht so aus, als müssten Sie in diesem Diktat regelmäßig Speicherplatz recyceln. Sie können das tun, indem Sie ein neues erstellen: new Dictionary<a,b>(oldDict). Stellen Sie sicher, dass dies auf threadsichere Weise geschieht.

Wann dies zu tun? Entweder auf dem Tick eines Timers (60sec?) Oder wenn eine bestimmte Anzahl von Schreibvorgängen aufgetreten ist (100k?) (Sie müssten einen Änderungszähler behalten).

0

Eine Lösung könnte sein, Clear() -Methode für das statische Wörterbuch aufzurufen. Auf diese Weise bleibt der Verweis auf das Wörterbuch verfügbar, aber die enthaltenen Objekte werden freigegeben.

+0

Jemand hat das in den Kommentaren erwähnt, und ich habe damals geantwortet, dass die 'Clear'-Methode nicht zu dem führt, was ich wollte. –

-2

Dies ähnelt einem Problem, das ich vor einiger Zeit beim Umgang mit großen DataTable-Objekten in einer DB-Anwendung festgestellt habe. Wenn Sie Clear in der DataTable aufrufen, werden alle Zeilen im Speicher mit Nullwerten belassen. So haben wir ein spezifisches Clearing- Verfahren wie:

someTable.Clear(); 
someTable.Dispose(); 
someTeble = new DataTable()... 

mit zusätzlichen Schritten die richtigen Spalten wieder in die neuen leeren, sauberen, Datatable hinzuzufügen.

Der Fluch in dieser wurde OFC, dass wir viel Code Referenzierung sometable hatten, die so angeordnet war immer und der Code auf die neue Instanz deutet nicht. Großes Chaos.

Meine Vermutung ist, dass unter der Haube sind diese nicht verwalteten Objekte mit einem NET-Wrapper auf ihnen und der Speicher wird nur freigegeben, wenn das Hauptobjekt zerstört wird.

Also, wenn Sie sie auf dynamische Weise verwenden, müssen Sie den Speicher selbst aufräumen.

Ich bin gerade in diesen Beitrag gelaufen, weil ich das gleiche Problem mit Hashtables sehe.

+0

Dies beantwortet die Frage nicht – Liam

Verwandte Themen