Skrócenie czasu pauzy JVM> 1 sekunda przy użyciu UseConcMarkSweepGC


10

Używam aplikacji intensywnie wykorzystującej pamięć na komputerze z 16 GB pamięci RAM i procesorem 8-rdzeniowym, a Java 1.6 wszystko działa w systemie CentOS w wersji 5.2 (wersja ostateczna). Dokładne szczegóły JVM są:

java version "1.6.0_10" 
Java(TM) SE Runtime Environment (build 1.6.0_10-b33) 
Java HotSpot(TM) 64-Bit Server VM (build 11.0-b15, mixed mode) 

jestem uruchomienie aplikacji z następujących opcji wiersza polecenia:

java -XX:+UseConcMarkSweepGC -verbose:gc -server -Xmx10g -Xms10g ... 

Moja aplikacja odsłania się JSON-RPC API, a moim celem jest odpowiadanie na wezwania w 25ms. Niestety, widzę opóźnienia do 1 sekundy i wydaje się, że są one spowodowane przez odśmiecanie. Oto niektóre z dłuższych przykładów:

[GC 4592788K->4462162K(10468736K), 1.3606660 secs] 
[GC 5881547K->5768559K(10468736K), 1.2559860 secs] 
[GC 6045823K->5914115K(10468736K), 1.3250050 secs] 

Każde z tych wydarzeń zbiórki śmieci towarzyszył opóźnionej reakcji API bardzo podobnej długości do długości zbierania śmieci pokazane (do kilku ms).

Oto kilka typowych przykładów (te były produkowane w ciągu kilku sekund):

[GC 3373764K->3336654K(10468736K), 0.6677560 secs] 
[GC 3472974K->3427592K(10468736K), 0.5059650 secs] 
[GC 3563912K->3517273K(10468736K), 0.6844440 secs] 
[GC 3622292K->3589011K(10468736K), 0.4528480 secs] 

Chodzi o to, że myślałem, że UseConcMarkSweepGC by tego uniknąć, albo przynajmniej uczynić go niezwykle rzadko. Przeciwnie, opóźnienia przekraczające 100 ms występują co najmniej raz na minutę lub dłużej (chociaż opóźnienia o ponad 1 sekundę są znacznie rzadsze, być może raz na 10 lub 15 minut).

Inną rzeczą jest to, że myślałem, że tylko PEŁNA GC spowoduje wstrzymanie wątków, ale nie wydaje się, aby były to pełne GC.

Może być istotne, aby pamiętać, że większa część pamięci jest zajęta przez pamięć podręczną LRU, która korzysta z miękkich odniesień.

Każda pomoc lub porada będą mile widziane.

+1

Co JDK/JRE i jaki system operacyjny? 22 lut. 092009-02-22 00:40:22

+1

Przepraszamy, ale dodaliśmy tę informację 22 lut. 092009-02-22 00:43:58

6

Okazuje się, że część sterty została zamieniona na dysk, więc odśmiecanie musiało wyciągnąć kilka danych z dysku z powrotem do pamięci.

Rozwiązałem to, ustawiając parametr "swappiness" Linuksa na 0 (aby nie zamienił danych na dysk).

+3

Zdaję sobie sprawę, że jest to od dawna, ale dla przyszłych odwiedzających: Przy takiej wielkości sterty rozważ możliwość włączenia wulkany dla Javy. Hugepages również nie mogą być wymieniane, więc rozwiążą problem z zamianą. 04 paź. 112011-10-04 14:43:08


0

Niektóre miejsca do rozpoczęcia poszukiwań:

Również chciałbym uruchomić kod poprzez profilera .. I jak ten w NetBeans ale są też inne. Możesz zobaczyć zachowanie gc w czasie rzeczywistym. Visual VM też to robi ... ale jeszcze go nie uruchomiłem (szukałem powodu, aby ... ale nie miałem jeszcze czasu ani potrzeby).


0

Proponuję również GCViewer i profilera.


11

Najpierw sprawdź dokumentację Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning, jeśli jeszcze tego nie zrobiłeś. Dokumentacja ta mówi:

równoczesne kolektor robi większość jego wykrywania i zamiatanie pracy z wątki aplikacja nadal działa, więc tylko krótkie przerwy są postrzegane przez wątków aplikacji.Jeśli jednak kolektor współbieżny nie jest w stanie ukończyć odzyskiwania nieosiągalnych obiektów przed wypełnieniem zamówionej generacji, lub jeśli nie można uzyskać przydziału z dostępnymi wolnymi blokami przestrzeni w pokoleniu o ustalonej liczbie miejsc w , aplikacja zostanie wstrzymana, a kolekcja zostaje zakończona z zatrzymanymi wszystkimi wątkami aplikacji. Brak możliwości uzupełnienia kolekcji równoczesnie jest określany jako błąd trybu współbieżnego i wskazuje na konieczność dostosowania parametrów kolektora współbieżnego.

i trochę później ...

Jednoczesne kolektor wstrzymuje wniosek dwukrotnie podczas jednoczesnego cyklu zbierania.

Zauważam, że te OWC nie wydają się uwalniać bardzo dużo pamięci. Być może wiele twoich obiektów jest długowiecznych? Możesz chcieć dostroić rozmiary generatorów i inne parametry GC. 10 Gig jest ogromną stertą z wielu standardów i naiwnie oczekiwałbym, że GC potrwa dłużej z tak ogromną stertą. Nadal 1 sekunda jest bardzo długim czasem pauzy i wskazuje, że coś jest nie tak (twój program generuje dużą liczbę zbędnych obiektów lub generuje trudne do odzyskania obiekty lub coś innego) lub po prostu trzeba dostroić GC.

Zazwyczaj chciałbym komuś powiedzieć, że jeśli mają dostroić GC, to mają inne problemy, które muszą najpierw rozwiązać. Ale przy zastosowaniu tej wielkości, myślę, że wpadasz w obszar "konieczności zrozumienia GC o wiele bardziej niż przeciętny programista".

Jak powiedzieli inni, musisz profilować aplikację, aby sprawdzić, gdzie znajduje się wąskie gardło. Czy twój PermGen jest za duży na przydzieloną mu przestrzeń? Czy tworzysz niepotrzebne obiekty? jconsole działa, aby przynajmniej pokazać minimum informacji o maszynie wirtualnej. To jest punkt wyjścia. Jak inni wskazali jednak, prawdopodobnie potrzebujesz bardziej zaawansowanych narzędzi niż to.

Powodzenia.


1

Oto kilka rzeczy, które znalazłem, które mogą być znaczące.

  • JSON-RPC może generować wiele obiektów. Nie tak bardzo jak XML-RPC, ale wciąż coś do oglądania. W każdym razie wydaje się, że generujesz tyle co 100 MB obiektów na sekundę, co oznacza, że ​​twój GC ma wysoki procent czasu i prawdopodobnie zwiększy losowe opóźnienie. Mimo że GC jest równoczesny, twój sprzęt/system operacyjny z dużym obciążeniem może wykazywać nieidealne opóźnienie losowe.
  • Zapoznaj się z architekturą Twojego banku pamięci. W systemie Linux polecenie to numactl --hardware. Jeśli twoja maszyna wirtualna jest dzielona na więcej niż jeden bank pamięci, to znacznie zwiększy twój GC. (Spowoduje to również spowolnienie działania aplikacji, ponieważ dostęp do nich może być znacznie mniej wydajny) Im cięższa praca podsystemu pamięci, tym bardziej prawdopodobne jest, że system operacyjny będzie musiał przesuwać pamięć (często w dużych ilościach), co powoduje dramatyczne przerwy (100 ms nie jest zaskakujące). Nie zapominaj, że Twój system operacyjny nie tylko uruchamia Twoją aplikację.
  • Rozważ kompaktowanie/zmniejszanie zużycia pamięci w pamięci podręcznej. Jeśli korzystasz z wielu GB pamięci podręcznej, warto przyjrzeć się sposobom zmniejszenia zużycia pamięci dalej niż już masz.
  • Proponuję, aby profilować swoją aplikację z jednoczesnym śledzeniem pamięci i próbkowaniem procesora. Może to przynieść bardzo różne wyniki i często wskazuje na przyczynę tego rodzaju problemów.

Stosując te podejścia, utajenie przez RPC można zmniejszyć do poniżej 200 mikro-sekundowych i czasami GC zredukowana do 1-3 ms wpływających mniej niż 1/300 połączeń.


11

Odkąd wspomniałeś o swoim pragnieniu buforowania, domyślam się, że większość twojej ogromnej sterty jest zajęta przez tę pamięć podręczną. Możesz chcieć ograniczyć rozmiar pamięci podręcznej, aby upewnić się, że nigdy nie zwiększy rozmiaru wystarczająco dużego, aby wypełnić pokolenie na stałe. Nie należy polegać wyłącznie na SoftReference, aby ograniczyć rozmiar. Ponieważ stare pokolenie wypełnia się miękkimi odniesieniami, starsze odniesienia zostaną usunięte i staną się śmieciami. Nowe odniesienia (być może do tej samej informacji) zostaną utworzone, ale szybko usunięte, ponieważ brakuje wolnego miejsca. Ostatecznie przestrzeń na czas jest pełna śmieci i musi zostać oczyszczona.

Należy również rozważyć dostosowanie ustawienia . Wartość domyślna to 1: 2, co oznacza, że ​​jedna trzecia sterty jest alokowana do nowej generacji. W przypadku dużej sterty prawie zawsze jest za dużo. Możesz spróbować czegoś podobnego do 9, które zachowałoby 9   Gb twojej sterty 10   Gb dla starej generacji.

+1

+1. To bardzo dobry punkt na temat -XX: NewRatio. 22 lut. 092009-02-22 03:25:04

  0

Tak, myślę, że to jest coś, czego wszyscy przegapimy podczas strojenia. 21 paź. 102010-10-21 08:37:21


0

Kilka rzeczy, które mam nadzieję, że może pomóc:

nigdy nie miałem dużo szczęścia z ConcurrentCollector, teoretycznie poświęca przepustowości dla przyrostu obniżonej latencji, ale znalazłem lepszego szczęścia zbieracz przepustowości zarówno dla przepustowości, jak i opóźnień (z dostrajaniem i dla moich aplikacji).

Twoja Pamięć podręczna miękkich odniesień jest trochę niebezpiecznym pomysłem dla kolekcjonerów kolekcjonerskich i jest prawdopodobnie jednym z powodów, dla których twoje kolekcje młodego pokolenia nie zbierają zbyt wielu śmieci.

Jeśli się nie mylę, bez względu na to, jak krótko trwa Obiekt, jeśli zostanie on umieszczony w pamięci podręcznej (która z pewnością trafiła do Czołowej Generacji), będzie żyła do momentu, w którym odbędzie się FullGC, nawet jeśli nie ma innych odniesień do niego!

Co to oznacza, że ​​twoje obiekty, które żyją w młodym genach, które są umieszczane w pamięci podręcznej, są teraz kopiowane wiele razy, utrzymywane przy życiu, podtrzymujące żywe referencje i ogólnie spowalniające młody GG GC.

Jest to swego rodzaju paradoksalne, jak buforowanie może zmniejszyć przydział obiektów, ale zwiększa czas GC.

Możesz także spróbować dostosować współczynnik przeżycia, może to być zbyt małe, przepełnienie jeszcze bardziej "młodych" obiektów w pokoleniu na stałe.


0

nie mam osobiście stosować taką ogromną kupę ale mam doświadczenie bardzo niską latencję w ogólnych z zastosowaniem następujących przełączników dla Oracle/Sun Java 1.6.x:

-Xincgc -XX:+UseConcMarkSweepGC -XX:CMSIncrementalSafetyFactor=50 
-XX:+UseParNewGC 
-XX:+CMSConcurrentMTEnabled -XX:ConcGCThreads=2 -XX:ParallelGCThreads=2 
-XX:CMSIncrementalDutyCycleMin=0 -XX:CMSIncrementalDutyCycle=5 
-XX:GCTimeRatio=90 -XX:MaxGCPauseMillis=20 -XX:GCPauseIntervalMillis=1000 

ważne części są, moim opinia, wykorzystanie CMS dla pokolenia na pokolenie i ParNewGC dla młodego pokolenia. Dodatkowo, dodaje to całkiem duży współczynnik bezpieczeństwa dla CMS (domyślnie jest to 10% zamiast 50%) i wymaga krótkich czasów pauzy. Ponieważ celujesz na czas reakcji wynoszący 25 ms, spróbuję ustawić -XX:MaxGCPauseMillis na jeszcze mniejszą wartość.Możesz nawet spróbować użyć więcej niż dwóch rdzeni do współbieżnego GC, ale zgadnę, że nie jest to warte użycia procesora.

Powinieneś prawdopodobnie również sprawdzić HotSpot JVM GC cheat sheet.