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, . 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. 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. 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 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. 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 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 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; 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 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 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 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. 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. 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 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.
A GC-k miatt kell aggódnunk?
Serial Garbage Collector
Párhuzamos szemétgyűjtő
CMS szemétgyűjtő
G1 Garbage Collector
Epsilon szemétgyűjtő
Z szemétgyűjtő
Shenandoah
Következtetés
Hivatkozás