Seven Types of JAVA Garbage Collectors
Tento dokument vysvětluje, co je to garbage collector v Javě a hlavní typy garbage collectorů s chováním jednotlivých garbage collectorů. Všimněte si také, že v tomto článku nevysvětluji, jak probíhá alokace haldy a spouštění hlavních/miniaturních garbage collectorů. To bude vysvětleno v dalším článku.
Java je objektově orientovaný programovací jazyk, který obsahuje automatický sběr odpadu. Java automaticky alokuje a dealokuje paměť, takže programy nejsou touto úlohou zatěžovány. Nyní (Java-12) má Java sedm typů garbage collectorů,
- Serial Garbage Collector
- Parallel Garbage Collector
- CMS Garbage Collector
- G1 Garbage Collector
- Epsilon Garbage Collector
- Z garbage collector
- Shenandoah Garbage Collector
.
5. a 6. garbage collector byly zavedeny v javě 11 a 7. byl zaveden v javě 12. Ve většině produkčních aplikací se používaly první čtyři typy garbage collectorů. Od nedávna přicházejí na řadu poslední tři typy garbage collectorů.
Musíme se o GC starat?
Ano, skutečně. Musíme se starat o GC a jeho chování. Protože může zajistit významný výkonnostní rozdíl. Každý GC má Své vlastní výhody a nevýhody. Jako vývojáři musíme mít jasnou představu o chování všech garbage collectorů a musíme vybrat garbage collector na základě našeho obchodního scénáře. Výběr garbage collectoru můžeme provést tak, že jej předáme jako argument JVM.
Serial Garbage Collector
Jedná se o nejjednodušší implementaci GC. Je v podstatě určen pro prostředí s jedním vláknem. Tato implementace GC při svém běhu zmrazí všechna aplikační vlákna. Používá jedno vlákno pro garbage collection. Proto není vhodné jej používat ve vícevláknových aplikacích, například v prostředí serverů.
Chceme-li zapnout Serial Garbage Collector, můžeme použít následující argument:
java -XX:+UseSerialGC -jar Application.java
Parallel Garbage Collector
Paralelní garbage collector se také nazývá jako průchozí kolektor. Na rozdíl od sériového garbage collectoru využívá ke sběru odpadu více vláken. Podobně jako sériový garbage collector i tento při provádění garbage collection zmrazí všechna aplikační vlákna. Tento garbage collector se nejlépe hodí pro ty aplikace, které snesou aplikační pauzy.
Pro zapnutí paralelního garbage collectoru můžeme použít následující argument:
java -XX:+UseParallelGC -jar Application.java
Použijeme-li tento GC, můžeme určit maximální počet vláken garbage collectoru a dobu pauzy, propustnost a footprint (velikost haldy)
Počty vláken garbage collectoru lze řídit pomocí volby příkazového řádku
-XX:ParallelGCThreads=<N>
Cílová doba maximální pauzy (mezera mezi dvěma GC)se určuje pomocí volby příkazového řádku
-XX:MaxGCPauseMillis=<N>
Cíl maximální propustnosti (měřený s ohledem na čas strávený prováděním garbage collection oproti času strávenému mimo garbage collection) je specifikován volbou příkazového řádku
-XX:GCTimeRatio=<N>
Maximální stopa haldy (množství paměti haldy, které program za běhu vyžaduje) se zadává pomocí volby -Xmx<N>.
Sběrač odpadků CMS
Sběrač odpadků CMS (Concurrent Mark Sweep) používá pro sběr odpadků více vláken. Prohledává paměť haldy, aby označil instance pro evikci, a poté označené instance zamete. Je určen pro aplikace, které dávají přednost kratším pauzám ve sběru odpadu a které si mohou dovolit sdílet prostředky procesoru se sběračem odpadu za běhu aplikace.
Sběrač odpadu CMS drží všechna vlákna aplikace pouze v následujících dvou scénářích
- Při označování odkazovaných objektů v prostoru staré generace.
- Každá změna v paměti haldy souběžně s prováděním garbage collection
V porovnání s paralelním garbage collector využívá CMS collector více CPU, aby zajistil lepší propustnost aplikace. Pokud můžeme alokovat více CPU pro lepší výkon, pak je CMS garbage collector vhodnější volbou než paralelní kolektor.
Pro zapnutí CMS garbage collector můžeme použít následující argument:
java -XX:+USeParNewGC -jar Application.java
G1 Garbage Collector
G1 (Garbage First) Garbage Collector je určen pro aplikace běžící na víceprocesorových strojích s velkým paměťovým prostorem. Je k dispozici od aktualizace JDK7 Update 4 a v pozdějších verzích.
Rozděluje paměť haldy na oblasti a provádí v nich paralelní sběr. G1 také provádí komprimaci volného prostoru haldy za chodu hned po znovuzískání paměti. Ale CMS garbage collector kompaktuje paměť v situacích zastavení světa (STW). Kolektor G1 nahradí kolektor CMS, protože je výkonnější.
Kolektor G1 obsahuje dvě fáze;
- Značení
- Zametání
Na rozdíl od ostatních kolektorů rozděluje kolektor G1 haldu na sadu stejně velkých oblastí haldy, z nichž každá představuje souvislý rozsah virtuální paměti. Při provádění sběru odpadu G1 vykazuje souběžnou fázi globálního označování, aby určil živost objektů v celé haldě.
Po dokončení fáze označování G1 ví, které oblasti jsou většinou prázdné. V těchto oblastech provádí sběr jako první, čímž obvykle získá značné množství volného místa. Proto se tento způsob sběru odpadu nazývá Garbage-First.
Chceme-li zapnout G1 Garbage Collector, můžeme použít následující argument:
java -XX:+UseG1GC -jar Application.java
Epsilon Garbage Collector
Epsilon je neoperativní neboli pasivní sběrač odpadu. Alokuje paměť pro aplikaci, ale nesbírá nepoužívané objekty. Když aplikace vyčerpá haldu Javy, JVM se vypne. To znamená, že Epsilon garbage collector umožňuje, aby aplikacím došla paměť a došlo k pádu.
Účelem tohoto garbage collectoru je měření a správa výkonu aplikace. Aktivní garbage collector je složitý program, který běží uvnitř JVM spolu s aplikací. Epsilon odstraňuje vliv GC na výkon. Neexistují žádné cykly GC ani překážky pro čtení a zápis. Při použití GC Epsilon běží kód izolovaně. Epsilon pomáhá vizualizovat, jak garbage collection ovlivňuje výkon aplikace a jaké jsou prahové hodnoty paměti, protože ukáže, kdy dojde. Jako příklad: Pokud si myslíme, že pro naši aplikaci potřebujeme pouze jeden gigabajt paměti, můžeme ji spustit s parametrem -Xmx1g a zjistit, jak se bude chovat. Pokud toto přidělení paměti nestačí, spusťte jej znovu s výpisem haldy. Upozorňujeme, že pro získání výpisu haldy musíme tuto volbu povolit. Tento argument můžeme použít k získání výpisu haldy, když aplikace padá kvůli nedostatku paměti.
XX:HeapDumpOnOutOfMemoryError
Pokud potřebujeme z naší aplikace vymáčknout každý kousek výkonu, může být Epsilon nejlepší volbou pro GC. Musíme však mít dokonalou představu o tom, jak náš kód využívá paměť. Pokud nevytváří téměř žádné smetí nebo přesně víte, kolik paměti využívá za dobu svého běhu, je Epsilon vhodnou volbou.
Chceme-li zapnout Epsilon Garbage Collector, můžeme použít následující argument:
java -XX:+UseEpsilonGC -jar Application.java
Z garbage collector
ZGC provádí veškerou nákladnou práci souběžně, aniž by zastavil provádění vláken aplikace na více než 10 ms, což je vhodné pro aplikace, které vyžadují nízkou latenci a/nebo používají velmi velkou haldu. Podle dokumentace společnosti Oracle si poradí s víceterabajtovými hromadami. Společnost Oracle zavedla ZGC v jazyce Java 11. Z garbage collector provádí své cykly ve svých vláknech. Aplikaci pozastaví v průměru na 1 ms. Kolektory G1 a Parallel mají průměr zhruba 200 ms.
V Javě 12 společnost Oracle přidala opravy výkonu a vykládání tříd, i když Z je stále v experimentálním stavu. Je k dispozici pouze v 64bitovém Linuxu. ZGC však využívá 64bitové ukazatele pomocí techniky zvané barvení ukazatelů. Barevné ukazatele uchovávají dodatečné informace o objektech na haldě. To je jeden z důvodů, proč je omezena na 64bitové JVM. V tomto článku byl tento scénář důkladně vysvětlen (https://www.opsian.com/blog/javas-new-zgc-is-very-exciting/).
ZGC provádí své značení ve třech fázích.
1. Označování ukazatelů se provádí ve třech fázích. Fáze krátkého zastavení světa – Zkoumá kořeny GC, lokální proměnné, které ukazují na zbytek haldy. Celkový počet těchto kořenů je obvykle minimální a neškáluje s velikostí zátěže, takže pauzy ZGC jsou velmi krátké a nezvyšují se s růstem haldy.
2. Souběžná fáze – Prochází graf objektů a zkoumá barevné ukazatele, čímž označuje dostupné objekty. Bariéra zatížení zabraňuje sporům mezi fází GC a činností jakékoli aplikace.
3. Fáze přemístění – Přesouvá živé objekty, aby uvolnila velké části haldy a urychlila alokace. Při zahájení relokační fáze rozdělí ZGC haldu na stránky a pracuje na jedné stránce po druhé. Jakmile ZGC dokončí přesouvání všech kořenů, zbytek relokace probíhá v souběžné fázi.
ZGC se pokusí sám nastavit počet vláken a obvykle má pravdu. Pokud však má ZGC příliš mnoho vláken, způsobí hladovění aplikace. Pokud jich bude mít málo, budete vytvářet smetí rychleji, než ho GC dokáže posbírat. Fáze ZGC ilustrují, jak spravuje velké haldy bez dopadu na výkon při nárůstu paměti aplikace.
Pro zapnutí Z Garbage Collectoru můžeme použít následující argument:
java -XX:+UseZGC -jar Application.java
Shenandoah
Shenandoah je sběrač odpadu s velmi nízkou dobou pauzy, který zkracuje dobu pauzy GC tím, že provádí více prací sběru odpadu současně se spuštěným programem Java. CMS i G1 provádějí souběžné označování živých objektů. Shenandoah přidává souběžné zhušťování.
Shenandoah používá oblasti paměti ke správě toho, které objekty se již nepoužívají a které jsou živé a připravené ke zhuštění. Shenandoah také přidává ke každému objektu haldy ukazatel předávání a používá jej k řízení přístupu k objektu. Návrh Shenandoah vyměňuje souběžné cykly procesoru a prostor za zlepšení času pauzy. Ukazatel forwarding usnadňuje přesouvání objektů, ale agresivní přesuny znamenají, že Shenandoah využívá více paměti a vyžaduje více paralelní práce než jiné GC. Práci navíc však vykonává s velmi krátkými pauzami typu stop-the-world.
Shenandoah zpracovává haldu v mnoha malých fázích, z nichž většina probíhá souběžně s aplikací. Tento návrh umožňuje GC efektivně spravovat velkou haldu.
- První stop-the-world pauza v cyklu. Připraví haldu pro souběžné označování a prohledá kořenovou množinu. Stejně jako uZGC odpovídá délka této pauzy velikosti kořenové množiny, nikoli haldy.
- Další, souběžná fáze prochází haldu a identifikuje dosažitelné a nedosažitelné objekty.
- Třetí dokončuje proces označování vyprázdněním čekajících aktualizací haldy a opětovným prohledáním kořenové množiny. Tato fáze spustí druhou pauzu cyklu zastavení světa. Počet čekajících aktualizací a velikost kořenové množiny určují, jak dlouhá tato pauza bude.
- Poté další souběžná fáze zkopíruje objekty z oblastí identifikovaných v poslední fázi označování. Tento proces odlišuje Shenandoah od ostatních GC, protože agresivně kompaktuje haldu paralelně s aplikačními vlákny.
- Další fáze spouští třetí (a nejkratší) pauzu v cyklu. Ta zajišťuje, že všechna vlákna GC dokončila evakuaci.
- Když skončí, souběžná fáze prochází haldu a aktualizuje reference na objekty přesunuté dříve v cyklu.
- Poslední pauza v cyklu ukončí aktualizaci referencí aktualizací kořenové množiny. Současně recykluje vyklizené oblasti.
- Nakonec poslední fáze znovu získá vyklizené oblasti, které nyní nemají žádné reference.
Můžeme nakonfigurovat Shenandoah s jednou ze tří heuristik. Ty určují, kdy GC zahájí své cykly a jakým způsobem vybírá regiony pro evakuaci.
1. Adaptivní: Sleduje cykly GC a zahajuje další cyklus tak, aby byl dokončen dříve, než aplikace vyčerpá haldu. Tato heuristika je výchozím režimem.
2. Statický:
3. Kompaktní: Spustí cyklus GC na základě obsazení haldy a alokačního tlaku: Spouští cykly GC nepřetržitě. Shenandoah spustí nový cyklus, jakmile předchozí skončí, nebo na základě množství alokované haldy od posledního cyklu. Tato heuristika způsobuje režii propustnosti, ale poskytuje nejlepší rekuperaci prostoru.
Shenandoah potřebuje shromažďovat haldu rychleji, než ji alokuje aplikace, které slouží. Pokud je alokační tlak příliš velký a není dostatek místa pro nové alokace, dojde k selhání. Shenandoah má pro tuto situaci konfigurovatelné mechanismy.
- Pacing: Pokud Shenandoah začne zaostávat za rychlostí alokace, pozastaví alokační vlákna, aby je dohnal. Zastavení obvykle stačí na mírné alokační špičky. Shenandoah zavádí zpoždění v délce 10 ms nebo méně. Pokud pacing selže, Shenandoah přejde k dalšímu kroku: degenerovanému GC.
- Degenerované GC: Pokud dojde k selhání alokace, Shenandoah zahájí fázi zastavení světa. Tuto fázi použije k dokončení aktuálního cyklu GC. Protože stop-the-world nesoupeří s aplikací o zdroje, měl by cyklus skončit rychle a odstranit nedostatek alokace. K degenerovanému cyklu často dochází až poté, co je většina práce cyklu již dokončena, takže stop-the-world je krátký. Protokol GC ji však bude hlásit jako úplnou pauzu.
- Úplný GC: Pokud selže jak krokování, tak degenerovaný GC, Shenandoah se vrátí k úplnému cyklu GC. Toto finální GC zaručuje, že aplikace neselže s chybou out-of-memory, pokud už nezbývá žádná halda.
Shenandoah nabízí stejné výhody jako ZGC s velkými hromadami, ale více možností ladění. V závislosti na povaze vaší aplikace se mohou hodit různé heuristiky. Jeho doby pauzy nemusí být tak krátké jako u ZGC, ale jsou předvídatelnější.
Pro zapnutí Shenandoah Garbage Collector můžeme použít následující argument:
java -XX:+UseShenanodoahC -jar Application.java
Závěr
Účelem tohoto článku je shrnout všechny garbage collectory. Proto byly některé části obsahu převzaty z uvedených odkazů. Musíme mít jasnou představu o garbage collectorech, abychom mohli vybrat optimální garbage collector pro případy použití naší aplikace. Optimální garbage collector výrazně zlepší výkon naší aplikace.
Reference
- https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
- https://www.baeldung.com/jvm-garbage-collectors
- https://dzone.com/articles/java-garbage-collection-3
- https://www.opsian.com/blog/javas-new-zgc-is-very-exciting/
- https://openjdk.java.net/projects/shenandoah/
- https://docs.oracle.com/en/java/javase/12/gctuning/z-garbage-collector1.html#GUID-A5A42691-095E-47BA-B6DC-FB4E5FAA43D0
- http://clojure-goes-fast.com/blog/shenandoah-in-production/
.