Syv typer af JAVA Garbage Collectors

Dette dokument vil forklare, hvad en garbage collector i java er, og de vigtigste typer af garbage collectors med adfærd for hver enkelt garbage collector. Bemærk også, at jeg i denne artikel ikke forklarer, hvordan heapallokering sker og hvordan større/minor garbage collectors starter. Det vil blive forklaret i den næste artikel.

Java er et objektorienteret programmeringssprog, der omfatter automatisk garbage collection. Java allokerer og deallokerer automatisk hukommelse, så programmerne ikke er belastet af denne opgave. Nu (Java-12) har Java syv typer garbage collectors,

  1. Serial Garbage Collector
  2. Parallel Garbage Collector
  3. CMS Garbage Collector
  4. G1 Garbage Collector
  5. Epsilon Garbage Collector
  6. Z garbage collector
  7. Shenandoah Garbage Collector

5. og 6. garbage collector er indført i java 11, og den 7. er indført i java 12. I de fleste applikationer i produktion har man brugt de første fire typer garbage collectors. Da de sidste tre garbage collectors for nylig er kommet ind i billedet.

Bør vi bekymre os om GC’er?

Ja, bestemt. Vi skal bekymre os om GC’en og dens adfærd. Fordi det kan give en betydelig forskel i ydeevnen. Hver GC har sine egne fordele og ulemper. Som udviklere skal vi have en klar idé om alle garbage collectors’ adfærd, og vi skal vælge en garbage collector ud fra vores forretningsscenarie. Vi kan vælge en garbage collector ved at overdrage valget som et JVM-argument.

Serial Garbage Collector

Dette er den enkleste GC-implementering. Den er grundlæggende designet til et miljø med en enkelt tråd. Denne GC-implementering fryser alle applikationstråde, når den kører. Den bruger en enkelt tråd til garbage collection. Derfor er det ikke en god idé at bruge den i flertrådede programmer som f.eks. servermiljøer.

For at aktivere Serial Garbage Collector kan vi bruge følgende argument:

java -XX:+UseSerialGC -jar Application.java

Parallel Garbage Collector

Den parallelle garbage collector kaldes også for en throughput collector. I modsætning til den serielle garbage collector bruger denne flere tråde til garbage collection. I lighed med den serielle garbage collector fryser denne også alle applikationstråde, mens den udfører garbage collection. Garbage Collector egner sig bedst til de applikationer, der kan tåle applikationspauser.

For at aktivere parallel Garbage Collector kan vi bruge følgende argument:

java -XX:+UseParallelGC -jar Application.java

Hvis vi bruger denne GC, kan vi angive maksimale garbage collection-tråde og pausetid, gennemløb og fodaftryk (heap-størrelse)

Antal af garbage collector-tråde kan styres med kommandolinjeindstillingen

-XX:ParallelGCThreads=<N>

Det maksimale pausetidsmål (mellemrum mellem to GC) angives med kommandolinjeindstillingen

-XX:

-XX:MaxGCPauseMillis=<N>

Det maksimale mål for gennemløb (målt med hensyn til den tid, der bruges til garbage collection, i forhold til den tid, der bruges uden for garbage collection) angives med kommandolinjeindstillingen

-XX:GCTimeRatio=<N>

Det maksimale heap-fodaftryk (mængden af heap-hukommelse, som et program kræver, mens det kører) angives ved hjælp af indstillingen -Xmx<N>.

CMS Garbage Collector

Concurrent Mark Sweep (CMS) garbage collector bruger flere garbage collector-tråde til garbage collection (affaldsindsamling). Den gennemsøger heap-hukommelsen for at markere instanser, der skal fjernes, og fejer derefter de markerede instanser. Den er designet til programmer, der foretrækker kortere affaldsindsamlingspauser, og som har råd til at dele processorressourcer med skraldemanden, mens programmet kører.

CMS skraldemanden holder kun alle programtråde i de følgende to scenarier

  1. Under markering af de refererede objekter i det gamle generationsrum.
  2. En hvilken som helst ændring i heap-hukommelsen parallelt med udførelse af garbage collection

I sammenligning med den parallelle garbage collector bruger CMS-opsamleren mere CPU for at sikre bedre applikationsgennemstrømning. Hvis vi kan allokere mere CPU for at opnå bedre ydeevne, er CMS garbage collector det foretrukne valg frem for den parallelle collector.

For at aktivere CMS Garbage Collector kan vi bruge følgende argument:

java -XX:+USeParNewGC -jar Application.java

G1 Garbage Collector

G1 (Garbage First) Garbage Collector er designet til applikationer, der kører på multiprocessormaskiner med stor hukommelsesplads. Den er tilgængelig siden JDK7 Update 4 og i senere udgivelser.

Den opdeler heap-hukommelsen i regioner og foretager indsamling inden for dem parallelt. G1 komprimerer også den frie heap-plads undervejs, lige efter at hukommelsen er blevet genbrugt. Men CMS garbage collector komprimerer hukommelsen i STW-situationer (stop the world). G1-opsamleren vil erstatte CMS-opsamleren, da den er mere præstationseffektiv.

I G1-opsamleren indeholder to faser;

  1. Marking
  2. Sweeping

I modsætning til andre opsamlere opdeler G1-opsamleren heap’en i et sæt af heap-regioner af samme størrelse, som hver er et sammenhængende område af virtuel hukommelse. Når G1 udfører skraldesamlinger, viser G1 en samtidig global mærkningsfase for at bestemme levetiden af objekter i hele heap’en.

Når mærkningsfasen er afsluttet, ved G1, hvilke regioner der for det meste er tomme. Den indsamler først i disse områder, hvilket normalt giver en betydelig mængde ledig plads. Det er derfor, at denne metode til garbage collection kaldes Garbage-First.

For at aktivere G1 Garbage Collector kan vi bruge følgende argument:

java -XX:+UseG1GC -jar Application.java

Epsilon Garbage Collector

Epsilon er en ikke-operativ eller en passiv garbage collector. Den allokerer hukommelsen til programmet, men den indsamler ikke de ubrugte objekter. Når programmet opbruger Java-heap’en, lukker JVM’en ned. Det betyder, at Epsilon garbage collector tillader, at applikationer løber tør for hukommelse og går ned.

Sigtet med denne garbage collector er at måle og styre applikationens ydeevne. Aktive garbage collectors er komplekse programmer, der kører inde i JVM’en sammen med dit program. Epsilon fjerner den indvirkning, som GC har på ydeevnen. Der er ingen GC-cyklusser eller læse- eller skrivebarrierer. Når du bruger Epsilon GC, kører koden i isolation. Epsilon hjælper med at visualisere, hvordan garbage collection påvirker appens ydeevne, og hvad er hukommelsestærsklen er, da den viser, når den løber ud. Som et eksempel Hvis vi tror, at vi kun har brug for en gigabyte hukommelse til vores applikation, kan vi køre den med -Xmx1g og se adfærden. Hvis denne hukommelsesallokering ikke er tilstrækkelig, kan vi køre den igen med et heap dump. Bemærk venligst, at vi skal aktivere denne indstilling for at få et heap dump. Vi kan bruge dette argument til at få et heap dump, når programmet går ned på grund af manglende hukommelse.

XX:HeapDumpOnOutOfMemoryError

Hvis vi skal presse hver en smule ydeevne ud af vores program, er Epsilon måske din bedste mulighed for en GC. Men vi er nødt til at have en fuldstændig forståelse af, hvordan vores kode bruger hukommelse. Hvis den næsten ikke skaber noget skrald, eller hvis du ved præcis, hvor meget hukommelse den bruger i den periode, den kører i, er Epsilon en brugbar mulighed.

For at aktivere Epsilon Garbage Collector kan vi bruge følgende argument:

java -XX:+UseEpsilonGC -jar Application.java

Z garbage collector

ZGC udfører alt dyrt arbejde samtidig uden at stoppe udførelsen af applikationstråde i mere end 10 ms, hvilket gør den velegnet til applikationer, der kræver lav latenstid og/eller bruger en meget stor heap. Ifølge Oracle-dokumentationen kan den håndtere heaps på flere terabyte. Oracle introducerede ZGC i Java 11. Z garbage collector udfører sine cyklusser i sine tråde. Den holder programmet på pause i gennemsnit 1 ms. G1- og Parallel-opsamlerne har et gennemsnit på ca. 200 ms.

I Java 12 tilføjede Oracle ydelsesrettelser og class unloading, selv om Z stadig er i eksperimentel status. Det er kun tilgængeligt på 64-bit Linux. Men ZGC drager fordel af 64-bit pointere med en teknik kaldet pointer coloring. Farvede pointere gemmer ekstra oplysninger om objekter på heap’en. Dette er en af grundene til, at den er begrænset til 64-bit JVM. Denne artikel har været forklaret dette scenarie dybt (https://www.opsian.com/blog/javas-new-zgc-is-very-exciting/).

ZGC foretager sin markering i tre faser.

1. Kort stop-the-world-fase – Den undersøger GC-rødderne, lokale variabler, der peger på resten af bunken. Det samlede antal af disse rødder er normalt minimalt og skalerer ikke med belastningens størrelse, så ZGC’s pauser er meget korte og øges ikke, efterhånden som din heap vokser.

2. Concurrent-fase – Den gennemgår objektgrafen og undersøger de farvede pointere og markerer tilgængelige objekter. Belastningsbarrieren forhindrer konkurrence mellem GC-fasen og enhver applikations aktivitet.

3. Relokaliseringsfase – Den flytter levende objekter for at frigøre store sektioner af heap’en for at gøre allokeringer hurtigere. Når relokeringsfasen begynder, opdeler ZGC heap’en i sider og arbejder på en side ad gangen. Når ZGC er færdig med at flytte eventuelle rødder, sker resten af relokaliseringen i en samtidig fase.

ZGC vil forsøge at indstille antallet af tråde selv, og det har som regel ret. Men hvis ZGC har for mange tråde, vil det udsulte dit program. Hvis den ikke har nok, skaber du garbage hurtigere, end GC kan indsamle det. ZGC’s faser illustrerer, hvordan den håndterer store heaps uden at påvirke ydeevnen, når programhukommelsen vokser.

For at aktivere Z Garbage Collector kan vi bruge følgende argument:

java -XX:+UseZGC -jar Application.java

Shenandoah

Shenandoah er en skraldesamler med ultralav pausetid, der reducerer GC-pausetiderne ved at udføre mere skraldesamlingsarbejde samtidig med det kørende Java-program. CMS og G1 udfører begge samtidig markering af levende objekter. Shenandoah tilføjer samtidig komprimering.

Shenandoah bruger hukommelsesregioner til at administrere, hvilke objekter der ikke længere er i brug, og hvilke der er levende og klar til komprimering. Shenandoah tilføjer også en forwarding pointer til hvert heap-objekt og bruger den til at styre adgangen til objektet. Shenandoahs design bytter samtidige CPU-cyklusser og plads til fordel for forbedringer af pausetiden. Forwarding pointeren gør det nemt at flytte objekter, men de aggressive flytninger betyder, at Shenandoah bruger mere hukommelse og kræver mere parallelt arbejde end andre GC’er. Men den udfører det ekstra arbejde med meget korte stop-the-world-pauser.

Shenandoah behandler heap’en i mange små faser, hvoraf de fleste er samtidige med programmet. Dette design gør det muligt for GC’en at håndtere en stor heap effektivt.

  1. Første stop-the-world pause i cyklusen. Den forbereder heap’en til samtidig markering og gennemgår rodsættet. LigesomZGC svarer længden af denne pause til størrelsen af rodsættet og ikke til heap’en.
  2. Dernæst gennemgår en samtidig fase heap’en og identificerer opnåelige og uopnåelige objekter.
  3. Den tredje afslutter markeringsprocessen ved at dræne ventende heap-opdateringer og scanne rodsættet på ny. Denne fase udløser den anden stop-the-world-pause i cyklussen. Antallet af ventende opdateringer og størrelsen af rodsættet bestemmer, hvor lang pausen er.
  4. Dernæst kopierer en anden samtidig fase objekterne ud af de regioner, der blev identificeret i den sidste markeringsfase. Denne proces adskiller Shenandoah fra andre GC’er, da den aggressivt komprimerer bunken parallelt med programtråde.
  5. Den næste fase udløser den tredje (og korteste) pause i cyklusen. Den sikrer, at alle GC-tråde har afsluttet evakueringen.
  6. Når den er færdig, gennemgår en samtidig fase heap’en og opdaterer referencerne til objekter, der er flyttet tidligere i cyklussen.
  7. Den sidste stop-the-world-pause i cyklussen afslutter opdateringen af referencerne ved at opdatere rodsættet. Samtidig genbruger den de evakuerede regioner.
  8. Den sidste fase genindtager til sidst de evakuerede regioner, som nu ikke har nogen referencer i dem.

Vi kan konfigurere Shenandoah med en af tre heuristikker. De styrer, hvornår GC starter sine cyklusser, og hvordan den udvælger regioner til evakuering.

1. Adaptiv: Observerer GC-cyklusser og starter den næste cyklus, så den afsluttes, før programmet udtømmer bunken. Denne heuristik er standardtilstanden.

2. Statisk: Starter en GC-cyklus baseret på heap-belægning og allokeringspres.

3. Kompakt: Kører GC-cyklusser kontinuerligt. Shenandoah starter en ny cyklus, så snart den foregående er afsluttet eller baseret på mængden af heap-allokeret siden den sidste cyklus. Denne heuristik medfører overhead for gennemløb, men giver den bedste genindvinding af plads.

Shenandoah skal indsamle heap hurtigere end det program, den betjener, allokerer den. Hvis allokeringspresset er for højt, og der ikke er plads nok til nye allokeringer, vil der ske en fejl. Shenandoah har konfigurerbare mekanismer til denne situation.

  • Pacing: Hvis Shenandoah begynder at sakke bagud i forhold til allokeringshastigheden, vil den standse allokeringstråde for at indhente det. Stallerne er normalt nok til milde allokeringsspidser. Shenandoah indfører forsinkelser på 10 ms eller mindre. Hvis pacing mislykkes, vil Shenandoah gå videre til næste trin: degenereret GC.
  • Degenereret GC: Hvis en allokeringsfejl opstår, starter Shenandoah en stop-the-world-fase. Den bruger fasen til at afslutte den aktuelle GC-cyklus. Da en stop-the-world ikke kæmper med programmet om ressourcerne, bør cyklussen afsluttes hurtigt og rydde op i tildelingsmanglen. Ofte opstår en degenereret cyklus efter at det meste af cyklens arbejde allerede er afsluttet, så stop-the-world er kort. GC-loggen vil dog rapportere det som en fuld pause.
  • Fuld GC: Hvis både pacing og en degenereret GC fejler, falder Shenandoah tilbage til en fuld GC-cyklus. Denne sidste GC garanterer, at programmet ikke fejler med en fejl uden for hukommelsen, medmindre der ikke er nogen heap tilbage.

Shenandoah tilbyder de samme fordele som ZGC med store heaps, men flere indstillingsmuligheder. Afhængigt af arten af dit program kan de forskellige heuristikker være velegnede. Dens pausetider er måske ikke så korte som ZGC’s, men de er mere forudsigelige.

For at aktivere Shenandoah Garbage Collector kan vi bruge følgende argument:

java -XX:+UseShenanodoahC -jar Application.java

Slutning

Det er hele formålet med denne artikel at opsummere alle garbage collectors. Derfor er nogle indholdsdele blevet uddraget fra de givne referencer. Vi er nødt til at have en klar idé om garbage collectors for at vælge en optimal garbage collector til vores applikationsbrugstilfælde. Den optimale garbage collector vil blive forbedret vores applikations ydeevne betydeligt.

Reference

  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/

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.