Wirtualna maszyna Java - dostrajanie GC
W ostatnim rozdziale dowiedzieliśmy się o różnych generacjach Gcs. W tym rozdziale omówimy, jak dostroić GC.
Rozmiar w biodrach
Rozmiar sterty jest ważnym czynnikiem wpływającym na wydajność naszych aplikacji Java. Jeśli jest za mały, to będzie się często wypełniał iw efekcie będzie musiał być często odbierany przez GC. Z drugiej strony, gdybyśmy po prostu zwiększyli rozmiar pryzmy, chociaż trzeba ją rzadziej zbierać, to długość przerw by się wydłużyła.
Ponadto zwiększenie rozmiaru sterty ma poważny wpływ na bazowy system operacyjny. Używając stronicowania, system operacyjny sprawia, że nasze aplikacje widzą znacznie więcej pamięci niż jest faktycznie dostępne. System operacyjny zarządza tym, wykorzystując miejsce wymiany na dysku, kopiując do niego nieaktywne części programów. Gdy te części są potrzebne, system operacyjny kopiuje je z powrotem z dysku do pamięci.
Załóżmy, że maszyna ma 8 GB pamięci, a JVM widzi 16 GB pamięci wirtualnej, JVM nie wiedziałaby, że w rzeczywistości jest tylko 8G w systemie. Po prostu zażąda 16G z systemu operacyjnego, a gdy uzyska tę pamięć, będzie nadal z niej korzystać. System operacyjny będzie musiał wymieniać wiele danych, co jest ogromnym spadkiem wydajności systemu.
A potem przychodzą przerwy, które wystąpiłyby podczas pełnego GC takiej pamięci wirtualnej. Ponieważ GC będzie działać na całej stercie w celu zbierania i kompaktowania, będzie musiał dużo czekać na wymianę pamięci wirtualnej z dysku. W przypadku współbieżnego kolektora wątki w tle będą musiały dużo czekać na skopiowanie danych z przestrzeni wymiany do pamięci.
Tutaj pojawia się pytanie, w jaki sposób powinniśmy zdecydować o optymalnym rozmiarze sterty. Pierwsza zasada to nigdy nie żądać od systemu operacyjnego większej ilości pamięci, niż jest w rzeczywistości. Pozwoliłoby to całkowicie uniknąć problemu częstej zamiany. Jeśli na komputerze jest zainstalowanych i uruchomionych wiele maszyn JVM, całkowite zapotrzebowanie na pamięć przez wszystkie z nich łącznie jest mniejsze niż rzeczywista pamięć RAM obecna w systemie.
Możesz kontrolować rozmiar żądania pamięci przez JVM za pomocą dwóch flag -
-XmsN - Kontroluje żądaną pamięć początkową.
-XmxN - Kontroluje maksymalną ilość pamięci, jakiej można zażądać.
Domyślne wartości obu tych flag zależą od bazowego systemu operacyjnego. Na przykład w przypadku maszyn JVM 64b działających w systemie MacOS -XmsN = 64M i -XmxN = minimum 1G lub 1/4 całkowitej pamięci fizycznej.
Należy pamiętać, że maszyna JVM może automatycznie dostosowywać te dwie wartości. Na przykład, jeśli zauważy, że dzieje się za dużo GC, będzie zwiększać rozmiar pamięci, o ile jest poniżej -XmxN i osiągane są pożądane cele wydajności.
Jeśli wiesz dokładnie, ile pamięci potrzebuje Twoja aplikacja, możesz ustawić -XmsN = -XmxN. W tym przypadku JVM nie musi ustalać „optymalnej” wartości sterty, a zatem proces GC staje się nieco bardziej wydajny.
Rozmiary generacji
Możesz zdecydować, ile sterty chcesz przeznaczyć na YG, a ile chcesz przeznaczyć na OG. Obie te wartości wpływają na wydajność naszych aplikacji w następujący sposób.
Jeśli rozmiar YG jest bardzo duży, wówczas byłby on zbierany rzadziej. Spowodowałoby to mniejszą liczbę obiektów awansowanych do OG. Z drugiej strony, jeśli zbytnio zwiększysz rozmiar OG, zbieranie i zagęszczanie zajmie zbyt dużo czasu, co doprowadzi do długich przerw w STW. Dlatego użytkownik musi znaleźć równowagę między tymi dwiema wartościami.
Poniżej znajdują się flagi, których możesz użyć do ustawienia tych wartości -
-XX:NewRatio=N: Stosunek YG do OG (wartość domyślna = 2)
-XX:NewSize=N: Początkowy rozmiar YG
-XX:MaxNewSize=N: Maksymalny rozmiar YG
-XmnN: Ustaw NewSize i MaxNewSize na tę samą wartość przy użyciu tej flagi
Początkowy rozmiar YG jest określany przez wartość NewRatio według podanego wzoru -
(total heap size) / (newRatio + 1)
Ponieważ wartość początkowa newRatio wynosi 2, powyższy wzór daje początkową wartość YG równą 1/3 całkowitego rozmiaru sterty. Zawsze możesz zmienić tę wartość, jawnie określając rozmiar YG za pomocą flagi NewSize. Ta flaga nie ma żadnej wartości domyślnej i jeśli nie zostanie ustawiona jawnie, rozmiar YG będzie nadal obliczany przy użyciu powyższego wzoru.
Permagen i Metaspace
Permagen i metaprzestrzeń to obszary sterty, w których JVM przechowuje metadane klas. Przestrzeń nazywana jest „permagenem” w Javie 7, aw Javie 8 nazywana jest „metaprzestrzenią”. Te informacje są używane przez kompilator i środowisko wykonawcze.
Możesz kontrolować rozmiar permagenu za pomocą następujących flag: -XX: PermSize=N i -XX:MaxPermSize=N. Rozmiar Metaspace można kontrolować za pomocą:-XX:Metaspace- Size=N i -XX:MaxMetaspaceSize=N.
Istnieją pewne różnice w zarządzaniu permagenem i metaprzestrzenią, gdy wartości flag nie są ustawione. Domyślnie oba mają domyślny rozmiar początkowy. Ale chociaż metaprzestrzeń może zajmować tyle sterty, ile potrzeba, permagen może zajmować nie więcej niż domyślne wartości początkowe. Na przykład maszyna JVM 64b ma 82 MB miejsca na stercie jako maksymalny rozmiar permagenu.
Zauważ, że ponieważ metaprzestrzeń może zajmować nieograniczoną ilość pamięci, chyba że zaznaczono inaczej, może wystąpić błąd braku pamięci. Pełne GC odbywa się za każdym razem, gdy zmieniany jest rozmiar tych regionów. Dlatego podczas uruchamiania, jeśli ładowanych jest wiele klas, metaprzestrzeń może zmieniać rozmiar, co za każdym razem powoduje otrzymanie pełnego GC. Dlatego uruchomienie dużych aplikacji zajmuje dużo czasu, jeśli początkowy rozmiar metaprzestrzeni jest zbyt mały. Warto zwiększyć rozmiar początkowy, ponieważ skraca to czas uruchamiania.
Chociaż permagen i metaprzestrzeń przechowują metadane klasy, nie są one trwałe, a przestrzeń jest odzyskiwana przez GC, jak w przypadku obiektów. Dzieje się tak zazwyczaj w przypadku aplikacji serwerowych. Za każdym razem, gdy wykonujesz nowe wdrożenie na serwerze, stare metadane muszą zostać wyczyszczone, ponieważ nowe programy ładujące klasy będą teraz potrzebować miejsca. Ta przestrzeń zostaje uwolniona przez GC.