Pytanie w sprawie .net, c#, garbage-collection – Jak odzyskać nieużywaną pamięć z dużej sterty obiektów LOH z wielu zarządzanych aplikacji?

8

Podczas rozmowy z kolegą o konkretnej grupie aplikacji wykorzystujących prawie 1,5 GB pamięci podczas uruchamiania ... wskazał mi bardzo dobry linkDebugowanie produkcji .NET

Część, która mnie zaintrygowała, to ...

Na przykład, jeśli przydzielisz 1 MB pamięci do pojedynczego bloku, sterta dużego obiektu rozszerzy się do rozmiaru 1 MB. Gdy zwolnisz ten obiekt, sterta dużego obiektu nie anuluje pamięci wirtualnej, więc sterta ma rozmiar 1 MB. Jeśli później przydzielisz kolejny blok o rozmiarze 500 KB, nowy blok zostanie przydzielony w bloku pamięci o wielkości 1 MB należącym do stosu dużych obiektów. W czasie trwania procesu sterta dużych obiektów zawsze rośnie, aby pomieścić wszystkie przydziały dużych bloków, do których obecnie się odwołuje, ale nigdy nie zmniejsza się, gdy obiekty są zwalniane, nawet jeśli wystąpi zbieranie śmieci. Rysunek 2.4 na następnej stronie pokazuje przykład sterty dużych obiektów.

Teraz załóżmy, że mamy fikcyjną aplikację, która tworzy lawinę dużych obiektów (> 85 KB), więc duża sterta obiektu rośnie, powiedzmy do 200 Meg. Teraz powiedzmy, że mamy 10 takich instancji aplikacji działających tak, że przydzielono 2000 Megs. Teraz ta pamięć nigdy nie jest zwracana do systemu operacyjnego, dopóki proces się nie zamknie ... (co zrozumiałem)

Czy są jakieś luki w moim rozumieniu? Jak odzyskać nieużywaną pamięć w różnych LOHeapach; nie tworzymy idealnej burzy wyjątków OutOfMemory?

Aktualizacja: Z odpowiedzi Marca chciałem wyjaśnić, że obiekty LOH nie są przywoływane - duże obiekty są używane -n-throw - jednak sterta nie kurczy się, nawet jeśli sterty są stosunkowo puste po początkowym przepływie.

Aktualizacja # 2: Po prostu dołączenie fragmentu kodu (przesadzonego, ale myślę o wszystkim). Widzę OutOfMemoryException w czasie, gdy pamięć wirtualna trafia na znak 1.5G na moim komputerze (1.7G na innym).Wpis na blogu Erica L., „pamięć procesu może być wizualizowana jako ogromny plik na dysku…” - wynik ten jest więc nieoczekiwany. Maszyny w tym przypadku miały GB wolnego miejsca na dysku twardym. Czy plik OS PageFile.sys (lub powiązane ustawienia) nakłada jakiekolwiek ograniczenia?

<code>        static float _megaBytes;
        static readonly int BYTES_IN_MB = 1024*1024;

        static void BigBite()
        {
           try
           {
              var list = new List<byte[]>();
              int i = 1;

              for (int x = 0; x < 1500; x++)
              {
                 var memory = new byte[BYTES_IN_MB + i];
                 _megaBytes += memory.Length / BYTES_IN_MB;
                 list.Add(memory);
                 Console.WriteLine("Allocation #{0} : {1}MB now", i++, _megaBytes);
              }
           }
           catch (Exception e)
           {  Console.WriteLine("Boom! {0}", e); // I put a breakpoint here to check the console
              throw;
           }
        }
       static void Main(string[] args)
        {
            BigBite();
            Console.WriteLine("Check VM now!"); Console.ReadLine();
            _megaBytes = 0;

            ThreadPool.QueueUserWorkItem(delegate { BigBite(); });
            ThreadPool.QueueUserWorkItem(delegate { BigBite(); });
            Console.ReadLine();   // will blow before it reaches here
        }
</code>
(odpowiedział na komentarz) Marc Gravell

Twoja odpowiedź

3   odpowiedź
2

jeśli naprawdę masz taki wzór alokacji, możesz przenieść swoje duże obiekty do innej domeny aplikacji - kiedy zdecydujesz się zwolnić wszystkie duże obiekty, zwolnij domenę aplikacji, a sterta dla tej domeny aplikacji zostanie zwolniona.

LOH nie jest przypisany do domeny aplikacji. mfawzymkh
3

to zależy od LOH - jednak nie zapominaj o tymOutOfMemoryException jest na proces, ponieważ naprawdę dysk twardy jest czynnikiem ograniczającym pamięć wirtualną. Eric Lippertblogował o tym niedawno. Oczywiście nie przeszkadza to w uzyskiwaniu słabej wydajności z całego stronicowania ...

Dzięki za link .. dołączył fragment kodu, który mnie bardziej myli, ale jest podobny do problemu, który rozpoczął ten wątek. Twoje myśli jak zawsze są bardzo cenione. Dziękujemy za pracę wykonaną na tym forum ... Gishu
Nie jestem pewien, czy edycja coś zmieni. Chodziło mi o to, że posiadanie piłki i łańcucha o rozmiarze 200 MB nie jestkoniecznie problem. Większość z nich prawdopodobnie wyjdzie z pamięci głównej z powodu braku aktywności ... Marc Gravell
Zaktualizuje pytanie: same obiekty LOH używają w tym przypadku -n-throw .. gdy już spełnią swoje zadanie, obiekty LOH zostały uwolnione, ale sterta nie kurczy się. Rozważmy komponent, który dokonuje serializacji obiektów przed wysłaniem ich w dół rury: w tym przypadku przydziela się powiedzmy 200 Meg jako tablice bajtowe, które są śmieciami po przesłaniu danych. Gishu
5

że używasz aplikacji jako aplikacji 32-bitowej, dostępna przestrzeń VA dla twojego procesu to tylko 2 GB, 3 GB, jeśli włączono duży przełącznik przestrzeni adresowej, więc nawet jeśli masz OGROMNY plik strony, nie ma znaczenia, czy masz 32-bitowy proces, to ma znaczenie, jeśli używasz 64-bitowego, gdzie masz ogromną przestrzeń adresową.

Obiekt o rozmiarze>85000 bajtów są przydzielane na LOH, zauważ to85000 bajtów nie 85K, mogą się też zmienić szczegóły wdrożenia. Wróćmy teraz do twojego pytania. GC anuluje zatwierdzenie segmentów LOH, które nie są używane w 2 sytuacjach 1- Gdy ciśnienie pamięci w maszynie jest wysokie (~ 95-98%) 2- Gdy nie uda się spełnić nowych żądań alokacji, anuluje nieużywane strony w LOH

więc odzyskasz pamięć w jednym z tych przypadków. Fakt, że uderzasz OOM przed osiągnięciem limitu 2 GB może oznaczać, że masz fragmentację VA, rozdrobnienie VA występuje, gdy nie masz ciągłej przestrzeni adresowej VA, aby zaspokoić nową alokację, na przykład pytasz o segment 8 KB, a ty nie t masz 2 kolejne strony w VA (zakładając, że rozmiar strony wynosi 4 K)

możesz użyć rozszerzenia debugera! vamap w narzędziach do debugowania dla okien, aby to sprawdzić.

Mam nadzieję, że to pomoże Dzięki

Powiązane pytania