[Popravljena verzija] 5 diagrama za potpuno razumevanje G1 sakupljača otpada
Kao efikasan sakupljač otpada, G1 se pridružio JVM-u u JDK7, a u JDK9 je zamenio CMS i postao podrazumevani sakupljač otpada.
1 Pregled sakupljača otpada
1.1 Nova generacija
Nova generacija koristi algoritam kopiranja, glavni sakupljači otpada su tri: Serial, Parallel New i Parallel Scavenge, karakteristike su sledeće:
- Serial: jednotreadni sakupljač, radi serijski, tokom GC-a svi ostali threadovi prestaju sa radom. Na single-core CPU-u, najveća efikasnost sakupljanja.
- Parallel New: višetreadna verzija Serial-a, podrazumevani sakupljač nove generacije. Na multi-core CPU-u, veća efikasnost, može da radi sa CMS sakupljačem.
- Parallel Scavenge: višetreadni sakupljač, više fokusiran na propusnost, pogodan za zadatke sa malo interakcije, ne može da radi sa CMS-om.
1.1 Stara generacija
- Serial Old: koristi algoritam označi-i-četkaj (kompresija), jednotreadno sakupljanje.
- Parallel Old: koristi algoritam označi-i-četkaj (kompresija), može da radi sa Parallel Scavenge
- CMS: Concurrent Mark Sweep, koristi algoritam označi-i-oberi, threadovi za sakupljanje mogu da rade zajedno sa korisničkim threadovima.
CMS nedostaci: niska propusnost, ne može da radi sa plutajućim otpadom, algoritam označi-i-oberi stvara velike fragmente memorije, nakon neuspeha konurentnog mode prelazi na Serial old.
- G1: deli heap na više Region-a jednakih veličina, nova i stara generacija nisu fizički izolovane, odlične performanse u scenario sa multi-core CPU-om i velikom memorijom. Nova generacija koristi algoritam kopiranja, stara generacija koristi algoritam označi-i-kompresuj (četkaj).
2 G1 uvod
2.1 Prvi susret sa G1
G1 sakupljač otpada se uglavnom koristi za scenario sa više procesora i velikom memorijom, ima pet atributa: generacijski, inkrementalni, paralelni (većinom može da bude konurentan), stop the word, označi-i-četkaj.
- Generacijski: kao i ostali sakupljači otpada, G1 deli heap na mladu generaciju i staru generaciju, sakupljanje otpada se uglavnom dešava u mladoj generaciji, i sakupljanje mlade generacije je najefikasnije. Povremeno se dešava i sakupljanje u staroj generaciji.
- Inkrementalni: kako bi STW vreme bilo kraće tokom sakupljanja otpada, G1 koristi inkrementalno i korak-po-korak sakupljanje. G1 analizira prethodno ponašanje aplikacije i vreme pauza da bi izgradio model predvidljivog vremena pauze, i koristi tu informaciju da predvidi sakupljanje otpada u okviru vremena pauze. Na primer: G1 će prvo sakupljati memorije regione sa visokom efikasnošću (ove regione većinom čine otpad koji se može sakupiti, zato se i zove G1).
- Paralelni i konurentni: kako bi povećao propusnost, neke operacije zahtevaju STW. Neke operacije koje traju dugo, na primer operacije nad celim heap-om (kao globalno označavanje) mogu se izvršavati konurentno, i mogu da se izvršavaju paralelno sa aplikacijom.
- Označi i četkaj: G1 uglavnom koristi algoritam označi i četkaj za sakupljanje otpada, faza označavanja je ista kao kod algoritma „označi i oberi“, ali nakon označavanja ne čisti direktno otpadne objekte, već pomeri sve žive objekte na jednu stranu, zatim direktno oslobodi memoriju van granice nakon pomeranja. Kao na sledećem dijagramu:

Znamo da je jedan od ciljeva sakupljača otpada da STW (stop the word) bude što kraći. Koristeći model predvidljivog vremena pauze, G1 postavlja ciljno STW vreme za sakupljanje otpada (kroz parametar -XX:MaxGCPauseMillis, podrazumevano 200ms), G1 se trudi da završi sakupljanje otpada u okviru tog vremena, i postiže visoku propusnost bez dodatne konfiguracije.
G1 teži da pronađe optimalnu ravnotežu između kašnjenja i propusnosti u sledećim aplikacijama i okruženjima:
- Veličina heap-a veća od 10GB, i više od polovine prostora zauzimaju živi objekti
- Tokom dugog rada sistema, brzina dodele i unapređenja objekata se brzo menja
- U heap-u postoji mnogo fragmenata memorije
- Vreme pauze tokom sakupljanja otpada ne sme prelaziti nekoliko stotina milisekundi, kako bi se izbeglo dugo vreme pauze uzrokovano sakupljanjem otpada.
Ako koristimo G1 u JDK8, možemo ga uključiti parametrom -XX:+UseG1GC.
G1 nije real-time sakupljač, on se trudi da maksimalno iskoristi performanse da bi završio pauzo vreme postavljeno sa MaxGCPauseMillis, ali ne može apsolutno da garantuje završetak u okviru tog vremena.
2.2 Raspored heap-a
G1 deli ceo heap na regione jednakih veličina, svaki region je kontinualna virtuelna memorija, region je osnovna jedinica za dodelu memorije i sakupljanje. Kao na sledećem dijagramu:

Crveni regioni sa „S" označavaju survivor mlade generacije, crveni bez „S" označavaju eden mlade generacije, svetlo plavi bez „H" označavaju staru generaciju, svetlo plavi sa „H" označavaju velike objekte u staroj generaciji. Za razliku od G1 prethodne strategije dodele memorije, survivor, eden, stara generacija - ovi regioni mogu biti nekontinualni.
G1 može da sakupi cele regione mlade generacije tokom pauze, objekti iz regiona mlade generacije se kopiraju ili u survivor region ili u regione stare generacije. Istovremeno, svaka pauza može da sakupi deo memorije stare generacije, kopirajući staru generaciju iz jednog regiona u drugi.
2.3 O regionima
U prethodnoj sekciji videli smo da je ceo heap memorije podeljen na više regiona jednakih veličina, svaki heap otprilike može imati 2048 regiona, svaki region je veličine 1~32 MB (mora biti stepen dvojke). Veličina regiona se podešava parametrom -XX:G1HeapRegionSize, tako da G1 po podrazumevanoj vrednosti može da upravlja maksimalno memorijom od 32MB * 2048 = 64G.
2.4 Veliki objekti
Veliki objekti su objekti čija je veličina veća od polovine regiona, veliki objekti mogu da se protežu preko više regiona, prilikom dodele memorije velikim objektima direktno se dodeljuju u staroj generaciji, ne dele se u eden regionu.
Kao na sledećem dijagramu, jedan veliki objekat zauzima dva i po regiona, prilikom dodele memorije velikom objektu mora se početi sa dodelom kontinualnih regiona od jednog regiona, pre nego što veliki objekat bude sakupljen, poslednji region ne može se dodeliti drugim objektima.

Kada se veliki objekti sakupljaju? Obično, tek nakon završetka označavanja u fazi čišćenja pauze ili tokom FullGC-a, mrtvi veliki objekti će biti sakupljeni. Ali, postoji izuzetak za velike nizove osnovnih tipova (na primer bool nizovi, svi celi nizovi, floating-point nizovi itd.), G1 će sakupiti ove mrtve velike objekte u bilo kojoj GC pauzi. Ova funkcija je podrazumevano uključena, ali može se isključiti parametrom -XX:G1EagerReclaimHumongousObjects.
Prilikom dodele velikih objekata, zbog prevelikog zauzeća prostora, može se prerov desiti GC pauza. G1 proverava da li je trenutno zauzeće heap-a veće od početnog praga zauzeća heap-a IHOP (The Initiating Heap Occupancy Percent) prilikom svake dodele velikog objekta, ako je trenutno zauzeće heap-a veće od IHOP praga, odmah se pokreće inicijalno označavanje. Više o inicijalnom označavanju u 4. sekciji.
Čak i tokom FullGC-a, veliki objekti se nikada ne pomeraju. Ovo može dovesti do preranog FullGC-a ili neočekivanog OOM-a, jer u tom trenutku iako postoji mnogo slobodne memorije, ta memorija su fragmenti u regionima.
3 Dodela memorije
Iako G1 deli heap memoriju na više regiona, i dalje postoje pojmovi mlade i stare generacije. G1 dodaje 2 parametra za kontrolu veličine memorije mlade generacije, -XX:G1NewSizePercent (podrazumevano 5), -XX:G1MaxNewSizePercent (podrazumevano 60). To znači da veličina mlade generacije podrazumevano zauzima 5% ~ 60% cele heap memorije.
Prema prethodnom uvodu, jedan heap može imati oko 2048 regiona, svaki region maksimalno 32M, tako da G1 može da upravlja maksimalno 64G heap-a, veličina mlade generacije je u opsegu 3.2G ~ 38.4G.
Za -XX:G1NewSizePercent i -XX:G1MaxNewSizePercent, treba obratiti pažnju na sledeća pitanja:
- Ako se podesi -Xmn, da li ova dva parametra imaju efekta?
Imaju efekat, na primer ako je heap veličine 64G, podesimo -Xmn3.2G, onda je ekvivalentno -XX:G1NewSizePercent=5 i -XX:G1MaxNewSizePercent=5, jer 3.2G/64G = 5%.
- Ako se podesi -XX:NewRatio, da li ova dva parametra imaju efekta?
Imaju efekat, na primer ako je heap veličine 64G, podesimo -XX:NewRatio=3, onda je ekvivalentno -XX:G1NewSizePercent=25 i -XX:G1MaxNewSizePercent=25. Jer mlada generacija : stara generacija = 1 : 3, to znači da mlada generacija zauzima 1/4 = 25%.
- Ako se podesi samo jedan od -XX:G1NewSizePercent i -XX:G1MaxNewSizePercent, da li ova dva parametra imaju efekta?
Podeseni parametar nema efekta, oba parametra koriste podrazumevane vrednosti.
- Ako oba parametra -XX:G1NewSizePercent i -XX:G1MaxNewSizePercent imaju efekta, kada dolazi do dinamičkog proširenja?
Ovo je u vezi sa parametrom -XX:GCTimeRatio. Ovaj parametar je integer između 0 i 100 (podrazumevano za G1 je 9, za ostale sakupljače je 99), ako je vrednost n, sistem neće potrošiti više od 1/(1+n) vremena na sakupljanje otpada. Stoga G1 podrazumevano troši maksimalno 10% vremena na sakupljanje otpada, ako vreme sakupljanja otpada premaši 10%, pokreće se proširenje. Ako proširenje ne uspe, pokreće se Full GC.
4 Sakupljanje otpada
Sakupljanje otpada u G1 se izvršava naizmenično u dve faze: Young-Only i Space-Reclamation. Kao na sledećem dijagramu:

U young-only fazi objekti postepeno popunjavaju regione stare generacije, space-reclamation faza osim što sakuplja memoriju mlade generacije, takođe inkrementalno sakuplja memoriju stare generacije. Nakon završetka, ponovo počinje young-only faza.
4.1 Young-only
Tok young-only faze je kao na sledećem dijagramu:

Ova faza počinje običnim young-only GC-om, young-only GC pomera neke objekte u staru generaciju, kada zauzeće prostora stare generacije dostigne IHOP, G1 prestaje sa običnim young-only GC-om i počinje inicijalno označavanje (Initial Mark).
- Inicijalno označavanje: u ovom procesu osim običnog young-only GC-a, takođe počinje proces konurentnog označavanja, ovaj proces određuje da će označeni živi objekti stare generacije biti zadržani u sledećoj space-reclamation fazi. Ovaj proces ne zahteva STW, moguće da obični young-only GC počne pre nego što se označavanje završi. Ovaj proces označavanja mora proći kroz procese ponovnog označavanja (Remark) i čišćenja (Cleanup) da bi se završio.
- Ponovno označavanje: ovaj proces zahteva STW, ovaj proces radi globalne reference i učitavanje klasa.
Između faza ponovnog označavanja i čišćenja G1 konurentno računa informacije o živim objektima, ove informacije se koriste za ažuriranje internih struktura podataka u fazi čišćenja.
- Faza čišćenja:
Ovaj čvor sakuplja sve slobodne regione, i odlučuje da li da nastavi sa jednim space-reclamation, ako da, izvršava samo jedan poseban young-only GC, young-only faza se završava.
O IHOP-u: podrazumevano, G1 posmatra koliko vremena je označavanje trajalo u ciklusima označavanja i koliko memorije je dodeljeno u staroj generaciji, time automatski određuje optimalni IHOP, ovo se zove adaptivni IHOP. Ako se ova funkcija uključi, jer na početku nema dovoljno posmatračkih podataka za određivanje IHOP-a, G1 koristi parametar -XX:InitiatingHeapOccupancyPercent za navođenje početnog IHOP-a. Može se isključiti adaptivni IHOP parametrom -XX:-G1UseAdaptiveIHOP, tada je IHOP fiksna vrednost navedena parametrom -XX:InitiatingHeapOccupancyPercent. Adaptivni IHOP podešava zauzeće stare generacije na sledeći način: kada zauzeće stare generacije = maksimalno zauzeće stare generacije - vrednost parametra -XX:G1HeapReservePercent, pokreće se prvi Mixed GC space-reclamation faze. Ovde parametar -XX:G1HeapReservePercent služi kao dodatna keš vrednost.
O označavanju: označavanje koristi SATB algoritam, na početku inicijalnog označavanja G1 čuva virtuelnu kopiju heap-a, objekti koji su živi u ovoj kopiji se takođe smatraju živima u sledećem procesu označavanja. Postoji problem, ako se deo objekata „ubije" tokom procesa označavanja, za space-reclamation fazu oni su i dalje „živi" (mada postoji mali izuzetak). U poređenju sa ostalim sakupljačima otpada, ovo može dovesti do toga da se deo mrtvih objekata pogrešno zadrži, ali daje bolju propusnost fazi označavanja, i ovi pogrešno zadržani objekti će biti sakupljeni u sledećoj fazi označavanja.
U young-only fazi treba sakupiti regione mlade generacije. Na kraju svakog young-only, G1 uvek podešava veličinu mlade generacije. G1 može koristiti parametre -XX:MaxGCPauseTimeMillis i -XX:PauseTimeIntervalMillis za podešavanje ciljnog vremena pauze, ova dva parametra su dobijena na osnovu dugotrajnog posmatranja stvarnog vremena pauze. On izračunava koliko vremena je potrebno za sakupljanje istog dela memorije mlade generacije na osnovu informacija o tome koliko se objekata kopira tokom GC-a i kako su objekti međusobno povezani.
Ako nema drugih ograničenja, G1 će podešiti veličinu young regiona na vrednost između -XX:G1NewSizePercent i -XX:G1MaxNewSizePercent kako bi zadovoljio zahtev za vremenom pauze.
4.2 Space-reclamation
Ova faza se sastoji od više Mixed GC-ova, ne samo sakuplja otpad mlade generacije, već i otpad stare generacije. Kada G1 uoči da sakupljanje više regiona stare generacije ne može osloboditi više slobodnog prostora, ova faza se završava. Nakon toga, periodično se ponovo pokreće nova Young-only faza.
Kada G1 nema dovoljno memorije prilikom sakupljanja informacija o živim objektima, G1 će napraviti Full GC i zahtevati STW.
U space-reclamation fazi, G1 će se truditi da sakupi što više moguće memorije stare generacije u okviru GC pauze. U ovoj fazi se veličina memorije mlade generacije podešava na minimalnu dozvoljenu vrednost parametrom -XX:G1NewSizePercent, sve dok postoje regioni stare generacije koji se mogu sakupiti, dodaju se u skup za sakupljanje, sve dok dodatno dodavanje ne prelazi ciljno vreme pauze. U određenoj GC pauzi, G1 će na osnovu efikasnosti sakupljanja regiona stare generacije (prioritet onih sa visokom efikasnošću) i preostalog slobodnog vremena dobiti konačni skup regiona za sakupljanje.
Broj regiona stare generacije za sakupljanje tokom svake GC pauze je ograničen brojem kandidata regiona podeljenim vrednošću parametra -XX:G1MixedGCCountTarget, parametar -XX:G1MixedGCCountTarget specificira maksimalan broj Mixed GC-ova u jednom ciklusu, podrazumevana vrednost je 8. Na primer ako -XX:G1MixedGCCountTarget koristi podrazumevanu vrednost 8, a skup kandidata regiona ima 200 regiona, tada se sakuplja 25 regiona po pauzi.
Skup kandidata regiona su svi regioni u staroj generaciji čije je zauzeće niže od -XX:G1MixedGCLiveThresholdPercent.
Kada zauzeće sakupljivog prostora u skupu regiona za sakupljanje postane niže od vrednosti parametra -XX:G1HeapWastePercent, Space-Reclamation se završava.
4.3 Situacija sa nestašicom memorije
Kada živi objekti aplikacije zauzmu mnogo memorije, tako da nema dovoljno prostora za kopiranje preostalih objekata, pokreće se evacuation failure. U tom trenutku G1 kako bi završio trenutno sakupljanje otpada, zadržava žive objekte koji su se već nalazili na novim pozicijama, ne pomera objekte koji nisu kopirani, samo podešava reference između objekata.
evacuation failure dovodi do dodatnog opterećenja, ali obično je brzo kao i ostali young GC. Nakon završetka evacuation failure, G1 nastavlja da izvršava aplikaciju kao i u normalnim uslovima. G1 pretpostavlja da se evacuation failure desio u kasnijoj fazi GC-a, u tom trenutku su se većina objekata već pomerala, i već postoji dovoljno memorije za nastavak izvršavanja aplikacije sve do završetka mark-a i početka space-reclamation. Ako ova pretpostavka ne važi (tj. nema dovoljno memorije za izvršavanje aplikacije), G1 konačno mora da pokrene Full GC, radi kompresiju celog heap-a, ovaj proces može biti vrlo spor.
5 Poređenje sa ostalim sakupljačima
5.1 Parallel GC
Parallel GC može da kompresuje i sakuplja memoriju stare generacije, ali može da radi samo sa celom starom generacijom. G1 inkrementalno raspodeljuje ceo GC posao na više kraćih pauza, naravno ovo može žrtvovati određenu propusnost.
5.2 CMS
Kao CMS, G1 konurentno sakuplja memoriju stare generacije, ali CMS koristi algoritam označi-i-oberi, ne radi sa fragmentacijom memorije stare generacije, konačno dovodi do dugačkog FullGC-a.
5.3 G1 problemi
Zbog korišćenja konurentnog sakupljanja, G1 performanse troškovi su veći, ovo može uticati na propusnost.
5.4 G1 prednosti
G1 može u bilo kojem GC periodu da sakuplja potpuno prazne memorije ili memorije sa velikim zauzećem u staroj generaciji. Ovo može izbeći neophodne GC-ove, jer može vrlo lako osloboditi velike količine memorije. Ova funkcija je podrazumevano uključena, može se isključiti parametrom -XX:-G1EagerReclaimHumongousObjects.
G1 može da bira paralelno uklanjanje duplikata String-ova iz celog heap-a. Ova funkcija je podrazumevano isključena, može se uključiti parametrom -XX:+G1EnableStringDeduplication.
6 Zaključak
Ovaj članak detaljno predstavlja G1 sakupljač otpada, nadam se da će vam pomoći da razumete G1.
Reference:
- https://docs.oracle.com/javase/10/gctuning/garbage-first-garbage-collector.htm#JSGCT-GUID-CE6F94B6-71AF-45D5-829E-DEADD9BA929D
