Mam klienta sieciowego, który przetwarza dane z serwera.Używanie stringowania w celu zmniejszenia użycia pamięci przez klienta sieciowego
Dane są wysyłane jako seria wiadomości, które same są kolekcjami klucz/wartość, podobnie jak koncept do nagłówków HTTP (z wyjątkiem braku "treści wiadomości"), tutaj jest typowy komunikat jednokierunkowy (linie oddzielone \r\n
):
Response: OK
Channel: 123
Status: OK
Message: Spectrum is green
Author: Gerry Anderson
Foo123: Blargh
Moje prace klienckie protokołu przez odczyt z NetworkStream
, znak po znaku za pomocą StreamReader
i while((nc = rdr.Read()) != -1)
i wykorzystuje parser najnowocześniejsze maszyny i StringBuilder
wystąpienie do wypełnienia Dictionary<String,String>
instancji. Te instancje słownika są następnie zapisywane w strukturach w pamięci w celu dalszego przetwarzania, z reguły mają one przydatny czas życia około 10 minut każdy.
Mój klient otrzymuje tysiące takich wiadomości na godzinę, a proces klienta trwa długo - jest to problem, ponieważ mój proces klienta często zużywa ponad 2 GB pamięci z tych instancji String
- użyłem windbg, aby zobaczyć gdzie Całe wspomnienie się skończyło. Jest to problem, ponieważ kod działa na maszynie wirtualnej Azure z tylko 3,5 GB pamięci. Nie widzę powodu, dla którego mój program powinien zużywać co najwyżej kilkaset MB pamięci RAM. Często umieszczam maszynę wirtualną i obserwuję zużycie pamięci mojego procesu w czasie i stopniowo wzrośnie ona do około 2 GB, a następnie nagle spadnie do około 100 MB, gdy GC zacznie pobierać kolekcję, a następnie wzrośnie ponownie. Czasy mogą się różnić między seriami GC, bez żadnej przewidywalności.
Ponieważ tak wiele z tych ciągów są identyczne (takie jak klucze Response
, Status
, etc), jak również znanych wartościach jak OK
i Fail
można używać internowanie łańcuchów w celu zmniejszenia zużycia, tak:
// In the state-machine parser after having read a Key name:
String key = stringBuilder.ToString();
key = String.Intern(key);
// etc... after reading value
messageDictionary.Add(key, value);
Problem polega na tym, że widzę miejsce na dodatkową optymalizację: sb.ToString()
zamierza przydzielić nową instancję napisów, która będzie używana do interwencji, a po drugie: internowane ciągi będą trwać przez całe życie aplikacji i niestety niektóre klucze wygrały " t zobacz ponownie użyć i faktycznie marnować pamięć, takie jak Foo123
w moim przykładzie protokołu. Jednym z rozwiązań, o którym myślałem, że nie jest użycie interakcji z ciągiem znaków, a zamiast tego jest klasa zawierająca static readonly
pól tekstowych, które są znanymi kluczami, a następnie używają normalnych, nie internowanych ciągów znaków - które ostatecznie mogłyby zostać wygenerowane GC, więc nie ryzykują wypełnienia do puli strun strun z jednostajnymi ciągami. Chciałbym następnie porównać instancję StringBuilder
z tymi znanymi ciągami, a jeśli tak, użyć ich zamiast wywoływać sb.ToString()
, pomijając w ten sposób kolejną alokację napisów.
Jednakże, jeśli zdecyduję się na internowanie każdego ciągu, puli intern będzie nadal rosła, i niestety .NET nie wydaje się mieć metoda .Chlorinate()
dla puli ciągów, czy istnieje sposób, aby usunąć jednorazowego użytku ciągi z puli intern, jeśli kontynuuję z podejściem String.Intern
, czy raczej korzystam z własnych statycznych instancji tylko do odczytu?
Może można symulować zachowanie tabeli referencyjnej bazy danych, używając jednego słownika z nazwą klucza i identyfikatorem, a drugiego z identyfikatorem i wartością? W ten sposób możesz naprawdę usunąć klucze na zawsze. – nvoigt
Użyj własnego interningu w razie potrzeby - nie ma sposobu na "unintern" napisów internowanych przez 'string.Intern'. Używanie odnośników do książek kodowych jest również dobrym sposobem na obniżenie zużycia pamięci. Zwróć uwagę, że stałe łańcuchowe są automatycznie internowane. – Luaan
Nie, interning nie jest rozwiązaniem tego problemu. I tak, zużycie pamięci powinno wynosić megabajty, a nie gigabajty. W twoim programie jest coś poważnie nie tak, trudno odgadnąć, co to może być. Nigdy nie rozprawiaj się z poważnymi problemami. Punktem wyjścia jest spojrzenie na kolekcje GC z Perfmon.exe, przyzwoity profiler pamięci jest lepszy. Skup się na utkniętym wątku finalizera lub przypiętych obiektach, będziesz chciał dowiedzieć się więcej o "asynchronicznych przypiętych uchwytach". –