2010-01-08 14 views
6

Mamy sytuację, w której rozważamy wymuszenie zbierania śmieci na serwerze, który ma bardzo mało pamięci RAM (średnio 3,6/4 GB). Nie, to naprawdę nie jest opcja, aby niestety zaktualizować ten serwer.. Usuwanie śmieci .NET - na co wpływa?

Jeden z naszych procesów serwisowych (napisany pośrednio w C++ (nie pytaj ...)) działa z komponentami .NET, a następnie śpi przez 10 minut. Kiedy usługa ta jest uśpiona, często zawiesza się na 600 MB pamięci RAM, którą można dzielić z innymi procesami. Wydaje się, że w jakiś sposób wiąże się to z włączaniem śledzenia GPW w celu debugowania. Mogę obejrzeć, jak się budzi i GC w następnej iteracji na pierwszym wywołaniu COM do .NET - jednak wtedy proces działa i po przejściu do trybu uśpienia użycie pamięci RAM powraca około 600 MB ... cóż, można zobaczyć, gdzie to idzie ...

Pytanie: Zastanawiam się nad dodaniem kolekcji śmieci tuż przed przejściem procesu uśpienia. W tym polu są inne usługi, które wykonują zadania związane z .NET. Kiedy wywołuję kolekcję śmieci w tym procesie obsługi, czy ta GC wpływa na wszystkie inne procesy związane z technologią .NET w polu lub po prostu proces żądający kolekcji? Trochę martwię się o problem z wydajnością dla procesów poza tym, na którym mi zależy.

+4

Odkładając na bok, jeśli przechowujesz 600 MB zasobów zarządzanych .NET przez 10 minut, zmuszając wywóz śmieci może nie uwolnić zasobów. Mogą nadal być w posiadaniu czegoś w samym procesie .NET. Czy na pewno nie masz żadnych aktywnych odnośników do tych zasobów? –

+0

Istnieje aktywne odniesienie do klasy wywołującej usługi sieci Web. Jednak kod C++ wywołuje funkcję po stronie .NET, która zwraca ciąg znaków. W klasie nie ma żadnych globalnych vars. Wszystkie zmienne powinny wykraczać poza zakres, gdy tylko wywołanie WS zostanie zakończone, a łańcuch zostanie zwrócony z powrotem do aplikacji C++. – TheToasterThatCould

+0

chciałem podziękować wszystkim, którzy przyczynili się ... jesteście niesamowici !! – TheToasterThatCould

Odpowiedz

10

Będzie to miało wpływ tylko na proces wywołujący GC.

+0

Odniesienie do tego byłoby przydatne. Dokumentacja, którą udało mi się znaleźć, mówi, że GC może wpływać na wszystkie domeny aplikacji lub tylko jedną. Zakładam, że kiedy go wymusisz, wpłynie to tylko na rozmówcę, ale byłoby miło to zobaczyć. –

+2

AppDomain! = Proces. AppDomain to wewnątrzprocesowa konstrukcja .NET, która ma różne ustawienia zabezpieczeń, stertę itp. To prawie mini-proces, który można wykorzystać do ładowania wtyczek z różnymi ustawieniami zabezpieczeń, itp. –

+0

Dobra uwaga - zapomniałem o wielu AppDomains w proces. –

4

Oprócz dodatkowego wykorzystania procesora na czas trwania kolekcji wymuszenie czyszczenia pamięci nie powoduje wpływu na inne procesy .NET. Jednak wymuszenie zbierania śmieci nie spowoduje - w większości przypadków - zwolnienia pamięci fizycznej.

Mapowanie pamięci fizycznej na przestrzeń adresową procesu jest skomplikowaną rzeczą. Zbiór śmieci w procesie .NET może zwiększyć ilość wolnego miejsca w zarządzanej sterty - ale nie gwarantuje to zmniejszenia rozmiaru zestawu roboczego (pamięć, z której proces Win32 pochodzi z systemu operacyjnego). Nawet gdyby udało się uzyskać taki system działający - byłby w najlepszym wypadku niestabilny i może nie działać w każdych okolicznościach.

Istnieje metoda Win32 o nazwie SetProcessWorkingSetSize(), która pozwala kontrolować minimalną i maksymalną pamięć fizyczną przydzieloną do procesu. Możesz zajrzeć do tego w połączeniu z wymuszonym systemem zbierania, który badasz.

8

Wpływa tylko na proces, do którego zadzwonił (ale wpłynie na cały proces Application Domains). Należy jednak pamiętać, że nie spowoduje to zwolnienia pamięci w systemie operacyjnym. Jeśli chcesz, aby zwolnić pamięć do systemu operacyjnego, to sposób, aby to zrobić jest:

private static void minimizeMemory() 
{ 
    GC.Collect(GC.MaxGeneration); 
    GC.WaitForPendingFinalizers(); 
    SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, 
     (UIntPtr) 0xFFFFFFFF, (UIntPtr) 0xFFFFFFFF); 
} 

[DllImport("kernel32.dll")] 
[return: MarshalAs(UnmanagedType.Bool)] 
private static extern bool SetProcessWorkingSetSize(IntPtr process, 
    UIntPtr minimumWorkingSetSize, UIntPtr maximumWorkingSetSize); 

Dużą zastrzeżenie tutaj jest dlaczego. Jeśli nie zauważysz, że pamięć jest dreptana lub zamiana jest wąskim gardłem wydajności (lub gdy piszesz aplikację użytkownika końcowego), nie ma powodu, by martwić się o wykorzystanie pamięci. Wymuszanie GC może być powolne. Zmniejszenie sterty spowoduje, że sterty będą musiały zostać ponownie przydzielone z maszyny wirtualnej. Ponadto .NET GC jest reaktywny, wykorzystując zachowanie programu, aby zwiększyć jego wydajność. Wymuszając kolekcję, nie pozwalasz GC dostroić się do zachowania twojego programu, co jeszcze bardziej zmniejsza wydajność.

Znam to z doświadczenia - spytałem się o numer this question, a nawet wymyśliłem klasę opartą na czasomierzu, aby wywołać powyższą metodę po opuszczeniu operacji wysokiej pamięci (uruchamianej co 5 minut). Następnie zestaw roboczy procesu znacznie się zmniejszył - zamieniając go na dysk. W ciągu następnych kilku sekund odtworzy najczęściej używane części, kilka minut później (gdy zadanie zostanie uruchomione ponownie), ponownie wykona wiązkę alokacji. Czas (nawet nie profilowanie) proces pokazał, że przy zbieraniu, było około 0,2 s (!) Spowolnienie podczas przydzielania VM z systemu operacyjnego dla zadania.

+0

Dlaczego ... ten proces serwisowy musi być uruchamiany co 10 minut. Skrzynka ma również interaktywną usługę (sorta jak citrix ... ale inna), która umożliwia użytkownikom łączenie się. Jednakże, gdy serwer ma mało pamięci RAM, ta usługa podobna do Citrix nie zezwoli na nowe połączenia. – TheToasterThatCould

+0

Ah, rozumiem. W rzeczy samej, będziesz chciał, aby proces zmniejszał rozmiar zestawu roboczego, a nie tylko robi GC (który po prostu scala stertę w procesie). –

5

Byłoby rozsądnie wyjąć profilera i sprawdzić, dlaczego obiekty nie są skutecznie zbierane przez śmieci. Być może kończą się one w gen2 bez potrzeby i można je refaktoryzować, aby szybciej pozbyć się obiektów.

Można również spróbować upewnić się, że część .NET działa z wersją serwerową garbage collectora, domyślną jest wersja stacji roboczej, a to woli utrzymywać proces aktywny niż uruchamiać GC (tzn. Jest ustawiony tak, aby zachować interfejs użytkownika czuły). Wersja serwera jest bardziej agresywna i działa równolegle GC, jeden na rdzeń.

Dodaj poniższe linie do app.config:

<Configuration> 
    <runtime> 
     <gcServer enabled=“true“ /> 
    </runtime> 
</Configuration> 

linky do more information

2

Wraz z innymi odpowiedziami nawiązujących bezpośrednio do korzystania z GC.Collect, chciałbym zaproponować Ci nie tylko niektóre proste profilowanie, ale także sprawdzenie kodu i upewnienie się, że prawidłowo udostępniasz zasoby po zakończeniu ich używania.

Czy wywołujesz utylizację na wszystkich obiektach, które implementują IDisposable po zakończeniu ich używania? Jeśli masz klasy, które zawierają prywatne zmienne, które implementują IDisposable, czy klasa zawierająca implementuje IDisposable i czy wywołujesz Dispose?

Czy są miejsca, w których można użyć instrukcji użycia, aby wywołać funkcję Dispose? Żadna z tych rzeczy nie pomoże w pamięci fizycznej, ale pomoże w takich rzeczach, jak UCHWYT do obiektów okiennych itp.

+0

Byłbym trochę zaskoczony, gdyby problem był z tym związany, ale i tak sprawdzę. Wtyczki COM .NET, które wywołujemy, uruchamiają proste funkcje, które sprawiają, że wywołanie usługi internetowej i zwraca ciąg znaków (xml z ws ogólnie). Nie mam zmiennych globalnych ani nie zarządzanych obiektów, o których jestem świadomy. – TheToasterThatCould

+0

COM? Następnie należy sprawdzić, czy komponenty COM są poprawnie zwalniane. Czy musisz wykonywać ręczne połączenia z Marshal.AddRef i Marshall.ReleaseComObject? – Nick

Powiązane problemy