Seitsemän JAVA:n roskienkerääjätyyppiä

Tässä asiakirjassa selitetään, mikä on roskienkerääjä Javassa ja roskienkerääjien päätyypit sekä kunkin roskienkerääjän käyttäytyminen. Huomaa myös, että tässä artikkelissa en selitä, miten kasan allokointi tapahtuu ja suuret/pienemmät roskankerääjät käynnistyvät. Se selitetään seuraavassa artikkelissa.

Java on oliosuuntautunut ohjelmointikieli, joka sisältää automaattisen roskienkeräyksen. Java varaa ja poistaa muistia automaattisesti, joten ohjelmia ei rasiteta tällä tehtävällä. Nyt (Java-12) Javassa on seitsemän erilaista roskienkerääjää,

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

  7. Z roskankerääjä
  8. Shenandoah roskankerääjä

5. ja 6. roskankerääjä on otettu käyttöön java 11:ssä ja 7. java 12:ssa. Useimmissa tuotantosovelluksissa on käytetty neljää ensimmäistä roskienkerääjätyyppiä. Koska kolme viimeistä roskankerääjää ovat hiljattain tulleet kuvaan.

Tarvitseeko meidän olla huolissamme GC:stä?

Kyllä, todellakin. Meidän täytyy huolehtia GC:stä ja sen käyttäytymisestä. Koska Se voi tarjota merkittävän suorituskykyeron. Jokaisella GC:llä on Sen omat edut ja haitat. Kehittäjinä meillä on oltava selkeä käsitys kaikkien roskienkerääjien käyttäytymisestä ja meidän on valittava roskienkerääjä liiketoimintaskenaariomme perusteella. Voimme valita roskienkerääjän välittämällä valinnan JVM-argumenttina.

Serial Garbage Collector

Tämä on yksinkertaisin GC-toteutus. Se on periaatteessa suunniteltu yhden säikeen ympäristöön. Tämä GC-toteutus jäädyttää kaikki sovellussäikeet, kun se suoritetaan. Se käyttää yhtä säiettä roskien keräämiseen. Siksi sitä ei kannata käyttää monisäikeisissä sovelluksissa, kuten palvelinympäristöissä.

Voidaksemme ottaa Serial Garbage Collectorin käyttöön voimme käyttää seuraavaa argumenttia:

java -XX:+UseSerialGC -jar Application.java

Parallel Garbage Collector

Rinnakkaista roskienkerääjää kutsutaan myös nimellä läpikulkukerääjä. Toisin kuin sarjamuotoinen roskienkerääjä, tämä käyttää useita säikeitä roskienkeruuseen. Samoin kuin sarjamuotoinen roskienkerääjä tämäkin jäädyttää kaikki sovellussäikeet roskienkeruun ajaksi. Roskienkerääjä soveltuu parhaiten niille sovelluksille, jotka kestävät sovellustaukoja.

Voidaksemme ottaa rinnakkaisen roskienkerääjän käyttöön, voimme käyttää seuraavaa argumenttia:

java -XX:+UseParallelGC -jar Application.java

Jos käytämme tätä GC:tä, voimme määrittää roskankeräyssäikeiden maksimimäärän ja tauko-ajan, läpäisykyvyn ja jalanjäljen (kasan koko)

Roskankeräyssäikeiden lukumäärää voidaan kontrolloida komentorivi-optiolla

-XX:ParallelGCThreads=<N>

Maksimi taukoaikatavoite (kahden GC:n välinen väli)määritetään komentorivi-optiolla

-XX:MaxGCPauseMillis=<N>

Maksimi läpimenoaikatavoite (mitataan suhteessa roskienkeräykseen käytettyyn aikaan verrattuna roskienkeräyksen ulkopuolella käytettyyn aikaan) määritetään komentorivivaihtoehdolla

-XX:GCTimeRatio=<N>

Maksimi heap footprint (ohjelman suorittamisen aikana tarvitseman heap-muistin määrä) määritetään optiolla -Xmx<N>.

CMS-roskankerääjä

Concurrent Mark Sweep (CMS) -roskankerääjä käyttää roskienkeruuseen useita roskankeräyssäikeitä. Se skannaa kasan muistia merkitäkseen instanssit häädettäväksi ja pyyhkäisee sitten merkityt instanssit. Se on suunniteltu sovelluksille, jotka haluavat lyhyempiä roskienkeräystaukoja ja joilla on varaa jakaa prosessoriresursseja roskienkerääjän kanssa sovelluksen ollessa käynnissä.

CMS-roskienkerääjä pitää kaikki sovellussäikeet vain seuraavissa kahdessa skenaariossa

  1. Merkitessään viitattuja objekteja vanhan sukupolven tilassa.
  2. Jokainen muutos kasan muistissa samanaikaisesti roskienkeräyksen tekemisen kanssa

Rinnakkaiseen roskienkerääjään verrattuna CMS-roskienkerääjä käyttää enemmän suorittimen tehoa paremman sovelluksen läpimenon varmistamiseksi. Jos voimme varata enemmän suorittimia parempaa suorituskykyä varten, CMS-roskankerääjä on parempi valinta kuin rinnakkaiskerääjä.

Voidaksemme ottaa CMS-roskankerääjän käyttöön voimme käyttää seuraavaa argumenttia:

java -XX:+USeParNewGC -jar Application.java

G1 Garbage Collector

G1 (Garbage First) Garbage Collector on suunniteltu sovelluksille, jotka toimivat moniprosessorikoneissa, joissa on paljon muistia. Se on käytettävissä JDK7 Update 4:stä lähtien ja myöhemmissä versioissa.

Se jakaa kasan muistin alueisiin ja tekee keräyksen niiden sisällä rinnakkain. G1 myös tiivistää vapaan heap-tilan liikkeessä heti muistin talteenoton jälkeen. Mutta CMS garbage collector tiivistää muistin stop the world (STW) -tilanteissa. G1-kerääjä korvaa CMS-kerääjän, koska se on suorituskykyisempi.

G1-kerääjässä on kaksi vaihetta;

  1. Merkintä
  2. Pyyhkäisy

Muista keräilijöistä poiketen G1-kerääjä jakaa kasan joukoksi samankokoisia kasan alueita, jotka ovat kukin virtuaalisen muistin vierekkäinen alue. Suorittaessaan roskienkeräystä G1 näyttää samanaikaisen globaalin merkitsemisvaiheen määrittääkseen objektien elävyyden koko kasassa.

Merkitsemisvaiheen päätyttyä G1 tietää, mitkä alueet ovat enimmäkseen tyhjiä. Se kerää ensin näiltä alueilta, mikä yleensä tuottaa huomattavan määrän vapaata tilaa. Siksi tätä roskienkeräysmenetelmää kutsutaan nimellä Garbage-First.

Voidaksemme ottaa G1-roskienkerääjän käyttöön voimme käyttää seuraavaa argumenttia:

java -XX:+UseG1GC -jar Application.java

Epsilon-roskienkerääjä

Epsilon-roskienkerääjä on ei-operatiivinen eli passiivinen. Se varaa sovellukselle muistia, mutta se ei kerää käyttämättömiä objekteja. Kun sovellus tyhjentää Javan kasan, JVM sammuu. Se tarkoittaa, että Epsilon-roskienkerääjä sallii, että sovelluksista loppuu muisti ja ne kaatuvat.

Tämän roskienkerääjän tarkoitus on mitata ja hallita sovelluksen suorituskykyä. Aktiiviset roskienkerääjät ovat monimutkaisia ohjelmia, jotka toimivat JVM:n sisällä sovelluksen rinnalla. Epsilon poistaa GC:n vaikutuksen suorituskykyyn. GC-syklejä tai luku- tai kirjoitusesteitä ei ole. Epsilon GC:tä käytettäessä koodi suoritetaan eristettynä. Epsilon auttaa visualisoimaan, miten roskienkeruu vaikuttaa sovelluksen suorituskykyyn ja mikä on muistin kynnysarvo, koska se näyttää, milloin se loppuu. Esimerkkinä Jos ajattelemme, että tarvitsemme vain yhden gigatavun muistia sovelluksellemme, voimme ajaa sen -Xmx1g:llä ja nähdä käyttäytymisen. Jos tuo muistinvaraus ei riitä, suorita se uudelleen heap dumpin kanssa. Huomaa, että meidän on otettava tämä vaihtoehto käyttöön saadaksemme heap dumpin. Voimme käyttää tätä argumenttia saadaksemme heapDumpin, kun sovellus kaatuu muistin loppumisen takia.

XX:HeapDumpOnOutOfMemoryError

Jos sovelluksestamme on puristettava jokainen bitti suorituskykyä, Epsilon saattaa olla paras vaihtoehto GC:lle. Meidän on kuitenkin ymmärrettävä täysin, miten koodimme käyttää muistia. Jos se ei luo juuri lainkaan roskia tai tiedämme tarkalleen, kuinka paljon muistia se käyttää aikavälillä, jolloin se toimii, Epsilon on varteenotettava vaihtoehto.

Voidaksemme ottaa Epsilon Garbage Collectorin käyttöön, voimme käyttää seuraavaa argumenttia:

java -XX:+UseEpsilonGC -jar Application.java

Z garbage collector

ZGC suorittaa kaikki kalliit työt samanaikaisesti pysäyttämättä sovellussäikeiden suoritusta yli 10 ms:n ajaksi, mikä tekee siitä sopivan sovelluksille, jotka vaativat pientä latenssia ja/tai käyttävät hyvin suurta kasaa. Oraclen dokumentaation mukaan se voi käsitellä monen teratavun kasoja. Oracle otti ZGC:n käyttöön Java 11:ssä. Z-roskankerääjä suorittaa syklinsä säikeissään. Se keskeyttää sovelluksen keskimäärin 1 ms:n ajaksi. G1- ja Parallel-roskankerääjät keskimäärin noin 200 ms.

Javaan 12 Oracle lisäsi suorituskykukorjauksia ja luokkien purkamisen, vaikka Z on edelleen kokeellisessa tilassa. Se on saatavilla vain 64-bittisessä Linuxissa. Mutta ZGC hyödyntää 64-bittisiä osoittimia tekniikalla nimeltä osoittimien väritys. Värilliset osoittimet tallentavat lisätietoa kasassa olevista objekteista. Tämä on yksi syy siihen, että se on rajoitettu 64-bittiseen JVM:ään. Tässä artikkelissa on selitetty tämä skenaario syvällisesti (https://www.opsian.com/blog/javas-new-zgc-is-very-exciting/).

ZGC tekee merkinnän kolmessa vaiheessa.

1. Lyhyt stop-the-world-vaihe – Se tutkii GC-juuret, paikalliset muuttujat, jotka osoittavat muuhun kasaan. Näiden juurien kokonaismäärä on yleensä minimaalinen, eikä se skaalaudu kuorman koon mukaan, joten ZGC:n tauot ovat hyvin lyhyitä, eivätkä ne kasva kasan kasvaessa.

2. Rinnakkaisvaihe – Se kävelee objektigraafin läpi ja tutkii värilliset osoittimet, jolloin se merkitsee saavutettavissa olevat objektit. Kuormitussulku estää GC-vaiheen ja minkä tahansa sovelluksen toiminnan välisen kilpailun.

3. Siirtovaihe – Se siirtää eläviä objekteja vapauttaakseen suuria osia kasasta, jotta allokaatiot nopeutuisivat. Kun siirtovaihe alkaa, ZGC jakaa kasan sivuihin ja työstää yhtä sivua kerrallaan. Kun ZGC on saanut valmiiksi kaikkien juurien siirtämisen, loput siirtämisestä tapahtuu samanaikaisessa vaiheessa.

ZGC yrittää itse asettaa säikeiden määrän, ja se on yleensä oikeassa. Mutta jos ZGC:llä on liian monta säiettä, se näännyttää sovelluksesi nälkään. Jos sillä ei ole tarpeeksi, luot roskia nopeammin kuin GC pystyy keräämään sitä. ZGC:n vaiheet havainnollistavat, miten se hallitsee suuria kasoja vaikuttamatta suorituskykyyn sovelluksen muistin kasvaessa.

Voidaksemme ottaa Z Garbage Collectorin käyttöön, voimme käyttää seuraavaa argumenttia:

java -XX:+UseZGC -jar Application.java

Shenandoah

Shenandoah on erittäin vähän taukoaikaa käyttävä roskienkerääjä, joka lyhentää GC:n taukoaikoja suorittamalla enemmän roskienkeruutyötä samanaikaisesti käynnissä olevan Java-ohjelman kanssa. CMS ja G1 suorittavat molemmat elävien objektien samanaikaisen merkitsemisen. Shenandoah lisää samanaikaista pakkaamista.

Shenandoah käyttää muistialueita hallinnoidakseen, mitkä objektit eivät ole enää käytössä ja mitkä ovat eläviä ja valmiita pakkaamiseen. Shenandoah lisää myös välitysosoittimen jokaiseen kasan objektiin ja käyttää sitä objektiin pääsyn hallintaan. Shenandoahin suunnittelussa vaihdetaan samanaikaisia suorittimen syklejä ja tilaa taukoaikojen parannuksiin. Välitysosoitin helpottaa objektien siirtämistä, mutta aggressiiviset siirrot tarkoittavat, että Shenandoah käyttää enemmän muistia ja vaatii enemmän rinnakkaista työtä kuin muut GC:t. Mutta se tekee ylimääräisen työn hyvin lyhyillä stop-the-world-tauoilla.

Shenandoah käsittelee kasaa monissa pienissä vaiheissa, joista suurin osa on yhtäaikaisia sovelluksen kanssa. Tämä rakenne mahdollistaa sen, että GC voi hallita suurta kasaa tehokkaasti.

  1. Ensimmäinen stop-the-world-tauko syklissä. Se valmistelee kasan samanaikaista merkintää varten ja skannaa juurijoukon. KutenZGC:ssä, tämän tauon pituus vastaa juurijoukon kokoa, ei kasan kokoa.
  2. Seuraavaksi rinnakkaisvaihe käy kasan läpi ja tunnistaa tavoitettavat ja saavuttamattomat kohteet.
  3. Kolmas päättää merkintäprosessin tyhjentämällä kasan odottavat päivitykset ja skannaamalla juurijoukon uudelleen. Tämä vaihe käynnistää syklin toisen stop-the-world-tauon. Vireillä olevien päivitysten määrä ja juurijoukon koko määräävät, kuinka pitkä tauko on.
  4. Sitten toinen samanaikainen vaihe kopioi objektit pois viimeisessä merkitsemisvaiheessa tunnistetuista alueista. Tämä prosessi erottaa Shenandoahin muista GC:istä, sillä se tiivistää kasaa aggressiivisesti rinnakkain sovellussäikeiden kanssa.
  5. Seuraava vaihe käynnistää syklin kolmannen (ja lyhimmän) tauon. Se varmistaa, että kaikki GC-säikeet ovat lopettaneet evakuoinnin.
  6. Kun se päättyy, rinnakkainen vaihe kävelee kasassa ja päivittää viittaukset aiemmin syklissä siirrettyihin objekteihin.
  7. Syklin viimeinen pysäytystauko päättää viittausten päivittämisen päivittämällä juurijoukon. Samalla se kierrättää evakuoidut alueet.
  8. Viimeiseksi viimeinen vaihe ottaa takaisin evakuoidut alueet, joissa ei nyt ole viitteitä.

Voidaan konfiguroida Shenandoah yhdellä kolmesta heuristiikasta. Ne määräävät, milloin GC aloittaa syklinsä ja miten se valitsee alueet evakuoitaviksi.

1. Adaptiivinen: Tarkkailee GC:n syklejä ja aloittaa seuraavan syklin niin, että se valmistuu ennen kuin sovellus tyhjentää kasan. Tämä heuristiikka on oletustila.

2. Staattinen: Käynnistää GC-syklin kasan käyttöasteen ja allokaatiopaineen perusteella.

3. Kompakti: Suorittaa GC-syklejä jatkuvasti. Shenandoah aloittaa uuden syklin heti edellisen päättyessä tai edellisen syklin jälkeen allokoidun kasan määrän perusteella. Tämä heuristiikka aiheuttaa läpäisykustannuksia, mutta tarjoaa parhaan tilan talteenoton.

Shenandoahin on kerättävä kasaa nopeammin kuin sitä palveleva sovellus allokoi sitä. Jos allokaatiopaine on liian suuri eikä tilaa riitä uusille allokaatioille, tapahtuu epäonnistuminen. Shenandoahilla on konfiguroitavia mekanismeja tätä tilannetta varten.

  • Pacing: Jos Shenandoah alkaa jäädä jälkeen allokaatiovauhdista, se pysäyttää allokaatiokierteet saadakseen ne kiinni. Pysäytykset riittävät yleensä lieviin allokaatiopiikkeihin. Shenandoah ottaa käyttöön enintään 10 ms:n viiveet. Jos tahdistus epäonnistuu, Shenandoah siirtyy seuraavaan vaiheeseen: degeneroitu GC.
  • Degeneroitu GC: Jos allokointi epäonnistuu, Shenandoah aloittaa stop-the-world-vaiheen. Se käyttää vaihetta nykyisen GC-syklin loppuun saattamiseen. Koska stop-the-world ei kilpaile sovelluksen kanssa resursseista, syklin pitäisi päättyä nopeasti ja poistaa allokaatiovaje. Usein degeneroitunut sykli tapahtuu sen jälkeen, kun suurin osa syklin työstä on jo suoritettu, joten stop-the-world on lyhyt. GC-loki raportoi sen kuitenkin täydeksi tauoksi.
  • Full GC: Jos sekä tahdistus että degeneroitunut GC epäonnistuvat, Shenandoah palaa takaisin täyteen GC-sykliin. Tämä lopullinen GC takaa, ettei sovellus epäonnistu muistin loppumisvirheeseen, ellei kasaa ole jäljellä.

Shenandoah tarjoaa samat edut kuin ZGC suurilla kasoilla, mutta enemmän viritysmahdollisuuksia. Sovelluksesi luonteesta riippuen eri heuristiikat voivat sopia hyvin. Sen taukoajat eivät ehkä ole yhtä lyhyitä kuin ZGC:n, mutta ne ovat ennustettavampia.

Voidaksemme ottaa Shenandoah Garbage Collectorin käyttöön voimme käyttää seuraavaa argumenttia:

java -XX:+UseShenanodoahC -jar Application.java

Johtopäätös

Tämän artikkelin tarkoituksena on tehdä yhteenveto kaikista roskienkerääjistä. Siksi joitakin sisällön osia on poimittu annetuista viitteistä. Meillä on oltava selkeä käsitys roskienkerääjistä, jotta voimme valita optimaalisen roskienkerääjän sovelluksemme käyttötapauksia varten. Optimaalinen roskankerääjä parantaa sovelluksemme suorituskykyä merkittävästi.

Viite

  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. https://docs.oracle.com/en/java/javase/12/gctuning/z-garbage-collector1.html#GUID-A5A42691-095E-47BA-B6DC-FB4E5FAA43D0
  8. http://clojure-goes-fast.com/blog/shenandoah-in-production/

Vastaa

Sähköpostiosoitettasi ei julkaista.