A JAVA szemétgyűjtők hét típusa

Ez a dokumentum elmagyarázza, mi a szemétgyűjtő a java-ban és a szemétgyűjtők fő típusai az egyes szemétgyűjtők viselkedésével. Kérjük, vegye figyelembe azt is, hogy ebben a cikkben nem magyarázom el, hogyan történik a halomkiosztás és hogyan indulnak el a major/minor szemétgyűjtők. Ezt a következő cikkben fogom elmagyarázni.

A java egy objektumorientált programozási nyelv, amely magában foglalja az automatikus szemétgyűjtést. A Java automatikusan kiosztja és felszabadítja a memóriát, így a programokat nem terheli ez a feladat. Most (Java-12) a Java hétféle szemétgyűjtővel rendelkezik,

  1. Serial Garbage Collector
  2. Parallel Garbage Collector
  3. CMS Garbage Collector
  4. G1 Garbage Collector
  5. Epsilon Garbage Collector
  6. .

  7. Z szemétgyűjtő
  8. Shenandoah szemétgyűjtő

5. és 6. szemétgyűjtő a java 11-ben, a 7. pedig a java 12-ben került bevezetésre. A legtöbb alkalmazásban a termelésben az első négy típusú szemétgyűjtőt használják. Mivel az utolsó három szemétgyűjtő nemrég került a képbe.

A GC-k miatt kell aggódnunk?

Igen, valóban. Aggódnunk kell a GC és a viselkedése miatt. Mert jelentős teljesítménykülönbséget biztosíthat. Minden GC-nek megvannak a maga előnyei és hátrányai. Fejlesztőként világos elképzelésünk kell, hogy legyen az összes szemétgyűjtő viselkedéséről, és az üzleti forgatókönyvünk alapján kell kiválasztanunk egy szemétgyűjtőt. A szemétgyűjtőt úgy választhatjuk ki, hogy a választást JVM argumentumként adjuk át.

Serial Garbage Collector

Ez a legegyszerűbb GC implementáció. Alapvetően egyszálas környezetre tervezték. Ez a GC implementáció lefutásakor az összes alkalmazásszálat befagyasztja. Egyetlen szálat használ a szemétgyűjtéshez. Ezért nem jó ötlet többszálú alkalmazásokban, például szerverkörnyezetben használni.

A soros szemétgyűjtő engedélyezéséhez a következő argumentumot használhatjuk:

java -XX:+UseSerialGC -jar Application.java

Párhuzamos szemétgyűjtő

A párhuzamos szemétgyűjtőt átviteli gyűjtőnek is nevezik. A soros szemétgyűjtőtől eltérően ez több szálat használ a szemétgyűjtéshez. A soros szemétgyűjtőhöz hasonlóan ez is lefagyasztja az összes alkalmazásszálat a szemétgyűjtés végrehajtása közben. A szemétgyűjtő leginkább azokhoz az alkalmazásokhoz alkalmas, amelyek elviselik az alkalmazásszüneteket.

A párhuzamos szemétgyűjtő engedélyezéséhez a következő argumentumot használhatjuk:

java -XX:+UseParallelGC -jar Application.java

Ha ezt a GC-t használjuk, megadhatjuk a maximális szemétgyűjtő szálakat és a szüneteltetési időt, az átviteli teljesítményt és a lábnyomot (heap méretét)

A szemétgyűjtő szálak számát a parancssori opcióval

-XX:ParallelGCThreads=<N>

A maximális szünetidő cél (két GC közötti különbség)a parancssori opcióval adható meg

-XX:MaxGCPauseMillis=<N>

A maximális áteresztőképességi célt (a szemétgyűjtéssel töltött idő és a szemétgyűjtésen kívül töltött idő viszonylatában mérve) a parancssori opcióval

-XX:GCTimeRatio=<N>

A maximális heap footprint (a program futás közben igényelt heap-memória mennyisége) a -Xmx<N> opcióval adható meg.

CMS szemétgyűjtő

A CMS (Concurrent Mark Sweep) szemétgyűjtő több szemétgyűjtő szálat használ a szemétgyűjtéshez. Átvizsgálja a halom memóriáját, hogy megjelöljön példányokat kilakoltatásra, majd a megjelölt példányokat lesöpri. Olyan alkalmazásokhoz tervezték, amelyek a rövidebb szemétgyűjtési szüneteket részesítik előnyben, és amelyek megengedhetik maguknak, hogy az alkalmazás futása közben a processzor erőforrásokat megosszák a szemétgyűjtővel.

A CMS szemétgyűjtő csak a következő két forgatókönyvben

  1. A hivatkozott objektumok jelölése során a régi generációs térben tartja az összes alkalmazásszálat.
  2. A halom memóriájának bármilyen módosítása a szemétgyűjtés elvégzésével párhuzamosan

A párhuzamos szemétgyűjtővel összehasonlítva a CMS gyűjtő több CPU-t használ a jobb alkalmazási átviteli teljesítmény biztosítása érdekében. Ha több CPU-t tudunk kiosztani a jobb teljesítmény érdekében, akkor a CMS szemétgyűjtő a párhuzamos gyűjtővel szemben előnyösebb választás.

A CMS szemétgyűjtő engedélyezéséhez a következő argumentumot használhatjuk:

java -XX:+USeParNewGC -jar Application.java

G1 Garbage Collector

G1 (Garbage First) Garbage Collector a többprocesszoros gépeken futó, nagy memóriaterülettel rendelkező alkalmazásokhoz készült. A JDK7 Update 4 óta és a későbbi kiadásokban is elérhető.

A heap memóriát régiókra osztja, és ezeken belül párhuzamosan végzi a gyűjtést. A G1 a memória visszakövetelése után a szabad heapterületet menet közben is tömöríti. De a CMS garbage collector stop the world (STW) helyzetekben tömöríti a memóriát. A G1 gyűjtő felváltja a CMS gyűjtőt, mivel teljesítményhatékonyabb.

A G1 gyűjtő két fázist tartalmaz;

  1. Marking
  2. Sweeping

A többi gyűjtőtől eltérően a G1 gyűjtő a kupacot egyenlő méretű kupac régiókra osztja fel, amelyek mindegyike a virtuális memória összefüggő tartománya. A szemétgyűjtés végrehajtásakor a G1 egyidejűleg globális jelölési fázist mutat be, hogy meghatározza az objektumok életképességét az egész halomban.

A jelölési fázis befejezése után a G1 tudja, hogy mely régiók nagyrészt üresek. Ezeken a területeken gyűjt először, ami általában jelentős mennyiségű szabad helyet eredményez. Ezért nevezik ezt a szemétgyűjtési módszert Garbage-Firstnek.

A G1 szemétgyűjtő engedélyezéséhez a következő argumentumot használhatjuk:

java -XX:+UseG1GC -jar Application.java

Epsilon szemétgyűjtő

Az Epszilon egy nem operatív vagy passzív szemétgyűjtő. Kiosztja a memóriát az alkalmazás számára, de a nem használt objektumokat nem gyűjti össze. Amikor az alkalmazás kimeríti a Java-heapet, a JVM leáll. Ez azt jelenti, hogy az Epsilon szemétgyűjtő lehetővé teszi, hogy az alkalmazások kifogyjanak a memóriából és összeomoljanak.

A szemétgyűjtő célja az alkalmazások teljesítményének mérése és kezelése. Az aktív szemétgyűjtők olyan összetett programok, amelyek a JVM-en belül futnak az alkalmazás mellett. Az Epsilon megszünteti a GC teljesítményre gyakorolt hatását. Nincsenek GC-ciklusok vagy olvasási és írási akadályok. Az Epsilon GC használata esetén a kód elszigetelten fut. Az Epsilon segít láthatóvá tenni, hogy a szemétgyűjtés hogyan befolyásolja az alkalmazás teljesítményét, és mi a memória küszöbérték, mivel megmutatja, mikor fogy el. Példaként ha úgy gondoljuk, hogy csak egy gigabájt memóriára van szükségünk az alkalmazásunkhoz, akkor futtathatjuk -Xmx1g-vel, és láthatjuk a viselkedést. Ha ez a memóriakijelölés nem elegendő, futtassuk le újra a heapdömpinggel. Vegyük figyelembe, hogy engedélyeznünk kell ezt az opciót ahhoz, hogy heap dumpot kapjunk. Ezt az argumentumot használhatjuk arra, hogy heapdumpot kapjunk, ha az alkalmazás a memória elfogyása miatt összeomlik.

XX:HeapDumpOnOutOfMemoryError

Ha minden kis teljesítményt ki kell sajtolnunk az alkalmazásunkból, az Epsilon lehet a legjobb választás a GC számára. De teljesen tisztában kell lennünk azzal, hogy a kódunk hogyan használja a memóriát. Ha szinte egyáltalán nem hoz létre szemetet, vagy pontosan tudjuk, hogy mennyi memóriát használ az adott futási időszakban, akkor az Epsilon egy járható út.

Az Epsilon szemétgyűjtő engedélyezéséhez a következő argumentumot használhatjuk:

java -XX:+UseEpsilonGC -jar Application.java

Z szemétgyűjtő

Az ZGC minden drága munkát egyidejűleg végez, anélkül, hogy 10 ms-nál hosszabb időre leállítaná az alkalmazás szálainak végrehajtását, ami alkalmassá teszi az alacsony késleltetést igénylő és/vagy nagyon nagy halmazt használó alkalmazásokhoz. Az Oracle dokumentációja szerint több terabájtos halmokat is képes kezelni. Az Oracle a Java 11-ben vezette be a ZGC-t. A Z szemétgyűjtő a ciklusait a szálaiban végzi. Az alkalmazást átlagosan 1 ms-ig szünetelteti. A G1 és a Parallel gyűjtők átlagosan nagyjából 200 ms-t.

A Java 12-ben az Oracle teljesítményjavításokat és osztályok kirakodását is hozzáadta, bár a Z még mindig kísérleti státuszban van. Csak 64 bites Linuxon érhető el. De a ZGC kihasználja a 64 bites mutatók előnyeit egy pointer coloring nevű technikával. A színezett mutatók extra információkat tárolnak a halomban lévő objektumokról. Ez az egyik oka annak, hogy csak a 64 bites JVM-re korlátozódik. Ebben a cikkben ezt a forgatókönyvet mélyen kifejtettük (https://www.opsian.com/blog/javas-new-zgc-is-very-exciting/).

A ZGC három fázisban végzi a jelölést.

1. Rövid stop-the-world fázis – Megvizsgálja a GC gyökereit, a heap többi részére mutató lokális változókat. Ezeknek a gyököknek a száma általában minimális, és nem skálázódik a terhelés méretével, így a ZGC szünetei nagyon rövidek, és nem nőnek a halom növekedésével.

2. Egyidejű fázis – Végigjárja az objektumgráfot, és megvizsgálja a színes mutatókat, megjelölve az elérhető objektumokat. A terhelésgátló megakadályozza a GC fázis és bármely alkalmazás tevékenysége közötti versengést.

3. Relokációs fázis – Élő objektumokat mozgat, hogy felszabadítsa a kupac nagy részeit, hogy az allokációk gyorsabbak legyenek. Az áthelyezési fázis megkezdésekor a ZGC oldalakra osztja a heapet, és egyszerre egy-egy oldalon dolgozik. Miután a ZGC befejezte bármelyik gyökér mozgatását, az áthelyezés többi része egy párhuzamos fázisban történik.

A ZGC megpróbálja maga beállítani a szálak számát, és általában igaza is van. De ha a ZGC-nek túl sok szála van, akkor éhezteti az alkalmazást. Ha nincs elég, akkor gyorsabban fogsz szemetet létrehozni, mint ahogy a GC össze tudja gyűjteni. A ZGC fázisai szemléltetik, hogyan kezeli a nagy halmokat anélkül, hogy az alkalmazás memóriájának növekedésével a teljesítményt befolyásolná.

A Z Garbage Collector engedélyezéséhez a következő argumentumot használhatjuk:

java -XX:+UseZGC -jar Application.java

Shenandoah

A Shenandoah egy ultraalacsony szünetidejű szemétgyűjtő, amely csökkenti a GC szünetidőt azáltal, hogy több szemétgyűjtési munkát végez a futó Java programmal egyidejűleg. A CMS és a G1 egyaránt az élő objektumok egyidejű jelölését végzi. A Shenandoah hozzáadja az egyidejű tömörítést.

A Shenandoah memóriarégiókat használ annak kezelésére, hogy mely objektumok nincsenek már használatban, és melyek az élő és tömörítésre kész objektumok. A Shenandoah továbbá minden heap-objektumhoz hozzáad egy továbbítási mutatót, és ezt használja az objektumhoz való hozzáférés ellenőrzésére. A Shenandoah tervezése az egyidejű CPU-ciklusokat és a helyet a szünetidő javulásáért cseréli el. A továbbító mutató megkönnyíti az objektumok mozgatását, de az agresszív mozgatások miatt a Shenandoah több memóriát használ és több párhuzamos munkát igényel, mint más GC-k. De a többletmunkát nagyon rövid világmegállási szünetekkel végzi.

A Shenandoah sok kis fázisban dolgozza fel a kupacot, amelyek többsége párhuzamos az alkalmazással. Ez a kialakítás lehetővé teszi, hogy a GC hatékonyan kezeljen egy nagy halmazt.

  1. Az első stop-the-world szünet a ciklusban. Előkészíti a kupacot az egyidejű jelölésre, és átvizsgálja a gyökérkészletet. AZGC-hez hasonlóan ennek a szünetnek a hossza a gyökérhalmaz méretének felel meg, nem a halom méretének.
  2. A következő, párhuzamos fázis bejárja a halmot és azonosítja az elérhető és elérhetetlen objektumokat.
  3. A harmadik befejezi a jelölési folyamatot a függőben lévő halomfrissítések leürítésével és a gyökérhalmaz újbóli átvizsgálásával. Ez a fázis váltja ki a ciklus második stop-the-world szünetét. A függőben lévő frissítések száma és a gyökérkészlet mérete határozza meg a szünet hosszát.
  4. Ezután egy másik párhuzamos fázis másolja ki az objektumokat az utolsó jelölési fázisban azonosított régiókból. Ez a folyamat különbözteti meg a Shenandoah-t a többi GC-től, mivel agresszíven tömöríti a kupacot az alkalmazásszálakkal párhuzamosan.
  5. A következő fázis a ciklus harmadik (és legrövidebb) szünetét váltja ki. Ez biztosítja, hogy az összes GC szál befejezte a kiürítést.
  6. Amikor ez befejeződik, egy párhuzamos fázis bejárja a heapet, és frissíti a ciklusban korábban áthelyezett objektumok referenciáit.
  7. A ciklus utolsó megálló szünete a gyökérkészlet frissítésével fejezi be a referenciák frissítését. Ezzel egyidejűleg újrahasznosítja a kiürített régiókat.
  8. Végül az utolsó fázis visszaköveteli a kiürített régiókat, amelyekben most már nincsenek hivatkozások.

A Shenandoah-t három heurisztika egyikével konfigurálhatjuk. Ezek szabályozzák, hogy a GC mikor kezdi a ciklusait, és hogyan választja ki az evakuálandó régiókat.

1. Adaptív: Figyelemmel kíséri a GC ciklusait, és úgy indítja el a következő ciklust, hogy az még azelőtt befejeződjön, mielőtt az alkalmazás kimerítené a halmazt. Ez a heurisztika az alapértelmezett mód.

2. Statikus: GC-ciklust indít a heap foglaltsága és az allokációs nyomás alapján.

3. Kompakt: Folyamatosan futtatja a GC-ciklusokat. A Shenandoah új ciklust indít, amint az előző befejeződik, vagy az előző ciklus óta kiosztott heap-mennyiség alapján. Ez a heurisztika átviteli többletköltséggel jár, de a legjobb helykihasználást biztosítja.

A Shenandoah-nak gyorsabban kell összegyűjtenie a heapet, mint ahogy az általa kiszolgált alkalmazás kiosztja azt. Ha az allokációs nyomás túl nagy, és nincs elég hely az új allokációkhoz, akkor hiba lép fel. A Shenandoah rendelkezik konfigurálható mechanizmusokkal erre a helyzetre.

  • Pacing: Ha a Shenandoah elkezd lemaradni az allokáció ütemétől, akkor a felzárkózás érdekében leállítja az allokációs szálakat. A leállások általában elegendőek az enyhe allokációs kiugrásokhoz. A Shenandoah legfeljebb 10 ms-os késleltetéseket vezet be. Ha a pacing nem sikerül, a Shenandoah a következő lépésre lép: degenerált GC.
  • Degenerált GC: Ha allokációs hiba történik, a Shenandoah egy stop-the-world fázist indít. A fázist az aktuális GC-ciklus befejezésére használja. Mivel a stop-the-world nem küzd az alkalmazással az erőforrásokért, a ciklusnak gyorsan be kell fejeződnie, és ki kell küszöbölnie az allokációs hiányt. Gyakran előfordul, hogy egy degenerált ciklus azután következik be, hogy a ciklus munkájának nagy része már befejeződött, így a stop-the-world rövid. A GC-napló azonban teljes szünetként fogja jelenteni.
  • Teljes GC: Ha mind a tempózás, mind a degenerált GC sikertelen, a Shenandoah visszatér a teljes GC-ciklushoz. Ez a végső GC garantálja, hogy az alkalmazás nem fog memórián kívüli hibával meghibásodni, kivéve, ha nincs több heap.

A Shenandoah ugyanazokat az előnyöket kínálja, mint a ZGC nagy heapekkel, de több hangolási lehetőséget. Az alkalmazásod jellegétől függően a különböző heurisztikák megfelelőek lehetnek. A szünetidők talán nem olyan rövidek, mint a ZGC-é, de kiszámíthatóbbak.

A Shenandoah szemétgyűjtő engedélyezéséhez a következő argumentumot használhatjuk:

java -XX:+UseShenanodoahC -jar Application.java

Következtetés

A cikk célja az összes szemétgyűjtő összefoglalása. Ezért egyes tartalmi részek a megadott hivatkozásokból kerültek kiragadásra. Világos képet kell kapnunk a szemétgyűjtőkről, hogy kiválaszthassuk az alkalmazásunk felhasználási eseteinek megfelelő optimális szemétgyűjtőt. Az optimális szemétgyűjtő jelentősen javítani fogja alkalmazásunk teljesítményét.

Hivatkozás

  1. https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
  2. https://www.baeldung.com/jvm-garbage-collectors
  3. https://dzone.com/articles/java-garbage-collection-3
  4. https://www.opsian.com/blog/javas-new-zgc-is-very-exciting/
  5. https://openjdk.java.net/projects/shenandoah/
  6. https://docs.oracle.com/en/java/javase/12/gctuning/z-garbage-collector1.html#GUID-A5A42691-095E-47BA-B6DC-FB4E5FAA43D0
  7. http://clojure-goes-fast.com/blog/shenandoah-in-production/

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.