2013-02-22 12 views
12

Mam pewne problemy z usługą WCF (niektóre zrzuty, wycieki pamięci itp.) I uruchamiam narzędzie profillng (ANTS Memory Profiles).Zwolnij pamięć ze słownika C# zawartego w obiekcie statycznym

Tylko po to, aby dowiedzieć się, że nawet po przetworzeniu (wykonuję konkretny test, a następnie zatrzymałem), Generacja 2 stanowi 25% pamięci dla usługi sieciowej. Wyszukałem tę pamięć, aby znaleźć, że mam obiekt słownikowy pełen (zero, zero) elementów, z -1-krotnym kodem.

Przepływ pracy usługi internetowej oznacza, że ​​podczas określonego przetwarzania elementy są dodawane, a następnie usuwane ze słownika (tylko proste Add i Remove). Nic takiego. Wydaje się jednak, że po usunięciu wszystkich elementów słownik jest pełen (zerowy, zerowy) s. KeyValuePair. Tysiące z nich tak naprawdę zajmują dużą część pamięci i ostatecznie dochodzi do przepełnienia, a odpowiednia wymuszona pula puli aplikacji i plik DW20.exe uzyskują wszystkie cykle procesora, jakie może uzyskać.

Słownik jest w rzeczywistości Dictionary<SomeKeyType, IEnumerable<KeyValuePair<SomeOtherKeyType, SomeCustomType>>> (System.OutOfMemoryException because of Large Dictionary), więc już sprawdziłem, czy istnieje jakiś rodzaj odniesienia, który trzyma rzeczy.

Słownik jest zawarty w statycznym obiekcie (aby był dostępny dla różnych wątków przetwarzania poprzez przetwarzanie), więc z tego pytania i wielu innych (Do static members ever get garbage collected?) Rozumiem, dlaczego ten słownik znajduje się w Generacji 2. Ale jest to również przyczyną tych (null, null)? Nawet jeśli usuwam przedmioty ze słownika, coś będzie zawsze zajęte w pamięci?

To nie jest problem z szybkością, jak w tym pytaniu Deallocate memory from large data structures in C#. Wydaje się, że pamięć nigdy nie zostaje odzyskana.

Czy jest coś, co mogę zrobić, aby usunąć elementy ze słownika, a nie tylko wypełniać je (zerowymi, zerowymi) parami? Czy jest coś jeszcze, co muszę sprawdzić?

+0

+ !: Za wykonanie odpowiednich badań i napisanie dobrego pytania. – Virtlink

+1

'List <>' ma 'TrimExcess()' metoda, 'Słownik <,>' niestety nie ma. Myślałem, że to był łatwy wynik :) –

+0

Czy wywołanie Dictionary.Clear() robi jakąkolwiek różnicę? – Alex

Odpowiedz

9

Słowniki przechowują elementy w tabeli skrótów. W tym celu używana jest wewnętrznie tablica. Ze względu na sposób działania tablic mieszających ta tablica musi zawsze być większa niż rzeczywista liczba przechowywanych elementów (co najmniej o około 30% większa). Microsoft stosuje współczynnik obciążenia 72%, tzn. Co najmniej 28% tablicy będzie puste (zob. An Extensive Examination of Data Structures Using C# 2.0, a zwłaszcza The System.Collections.Hashtable Class i The System.Collections.Generic.Dictionary Class). Dlatego wpisy null/null mogą reprezentować tę wolną przestrzeń.

Jeśli tablica jest zbyt mała, będzie rosnąć automatycznie; jednak po usunięciu elementów tablica nie kurczy się, ale przestrzeń, która zostanie zwolniona, powinna zostać ponownie wykorzystana po wstawieniu nowych elementów.

Jeśli jesteś w kontroli tego słownika, można spróbować odtworzyć go w celu jej kurczenia:

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

Ale problem może wynikać z rzeczywistych (nie pusty) wpisów. Twój słownik jest statyczny i dlatego nigdy nie będzie automatycznie odzyskiwany przez odśmiecacz, chyba że przypiszesz mu inny słownik lub null (lub theDict = null).Odnosi się to tylko do samego słownika, który jest statyczny, a nie do jego wpisów. Dopóki odwołania do usuniętych wpisów istnieją gdzie indziej, będą się utrzymywać. GC odbierze dowolny obiekt (wcześniej lub później), do którego nie można uzyskać dostępu za pomocą jakiegoś odnośnika. Nie ma znaczenia, czy obiekt ten został uznany za statyczny, czy nie. Same obiekty nie są statyczne, tylko ich odniesienia.

+0

Dziękuję za informację, sprawy stają się jaśniejsze. Chociaż widzę zalety dostępnej wolnej pamięci, chciałbym ją "skurczyć" w sposób sugerowany przez ciebie i @usr. Po prostu muszę lepiej analizować, kiedy chcę to zrobić. –

+0

@Olivier Mówiąc, że nie kurczy się, gdy elementy są usuwane, oznacza to, że pamięć będzie nadal używana? –

+0

@Siraj Mansour: Tak. –

4

Wygląda na to, że musisz okresowo odzyskiwać miejsce w tym dyktowaniu. Możesz to zrobić, tworząc nowy: new Dictionary<a,b>(oldDict). Pamiętaj, aby zrobić to w sposób bezpieczny dla wątków.

Kiedy to zrobić? Albo na haczyku licznika czasu (60 sekund?), Albo gdy wystąpiła określona liczba zapisów (100k?) (Trzeba zachować licznik modyfikacji).

0

Rozwiązaniem może być wywołanie metody Clear() w słowniku statycznym. W ten sposób odniesienie do słownika pozostanie dostępne, ale zawarte w nim obiekty zostaną zwolnione.

+0

Ktoś wspomniał o tym w komentarzach, a ja odpowiedziałem wtedy, że użycie metody "Wyczyść" nie doprowadziło do tego, co chciałem. –

-2

Jest to podobne do problemu, który znalazłem podczas pracy z dużymi obiektami DataTable w aplikacji DB jakiś czas temu. Po prostu wywołanie opcji Wyczyść na DataTable pozostawiło wszystkie wiersze w pamięci z wartościami pustymi. Dlatego wdrożyliśmy konkretną metodę rozliczania takiego:

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

z dodatkowych kroków, aby dodać odpowiednią kolumn z powrotem do nowego pustego, czystego, DataTable.

Klątwa w tym to ofc, że mieliśmy mnóstwo kodu odwołującego się do someTable, który został usunięty, a kod nie wskazywał na nową instancję. Wielki bałagan.

Domyślam się, że pod maską znajdują się niezarządzane obiekty z obwolutą .net, a pamięć jest zwalniana dopiero po zniszczeniu głównego obiektu.

Więc jeśli używasz ich w dynamiczny sposób, musisz sam wyczyścić pamięć.

Właśnie natknąłem się na ten post, ponieważ widzę ten sam problem z hashtables.

+0

To nie odpowiada na pytanie – Liam

Powiązane problemy