Vodič za System.gc ()

1. Pregled

U ovom uputstvu istražit ćemo System.gc () metoda smještena u java.lang paket.

Eksplicitno pozivanje System.gc () je poznat po lošoj praksi. Pokušajmo shvatiti zašto i postoje li slučajevi upotrebe kada bi pozivanje ove metode moglo biti korisno.

2. Odvoz smeća

Java virtualni stroj odlučuje izvršiti prikupljanje smeća kad postoje naznake za to. Te se indikacije razlikuju od jedne do druge provedbe GC-a. Oni se temelje na različitim heuristikama. Međutim, postoji nekoliko trenutaka kada će se GC sigurno izvršiti:

  • Nova generacija (Tenured space) je puna, što pokreće manji GC
  • Stara generacija (prostori Eden + Survivor0 + Survivor1) je puna, što pokreće glavni / cjeloviti GC

Jedino što je neovisno o provedbi GC-a jest prihvatljivost predmeta za odvoz smeća.

Sada ćemo pogledati System.gc () sama metoda.

3. System.gc ()

Pozivanje metode je jednostavno:

System.gc ()

Službena Oracleova dokumentacija navodi da:

Pozivanje gc metoda sugerira da Java virtualni stroj troši napore na recikliranje neiskorištenih objekata kako bi memoriju koju trenutno zauzimaju učinio dostupnom za brzu ponovnu upotrebu.

Ne postoji jamstvo da će se aktivirati stvarni GC.

System.gc () pokreće glavni GC. Stoga postoji rizik da ćete potrošiti neko vrijeme na zaustavljanje svijeta, ovisno o vašoj primjeni sakupljača smeća. Kao rezultat, imamo nepouzdan alat s potencijalno značajnom kaznom izvedbe.

Postojanje eksplicitnog poziva na odvoz smeća trebalo bi biti ozbiljna crvena zastava za sve.

Možemo spriječiti System.gc () od obavljanja bilo kakvog posla pomoću -XX: DisableExplicitGC JVM zastava.

3.1. Ugađanje izvedbe

Vrijedno je napomenuti da je neposredno prije bacanja OutOfMemoryError, JVM će izvesti puni GC. Stoga izričit poziv na System.gc () neće nas spasiti od neuspjeha.

Sakupljači smeća danas su stvarno pametni. Oni posjeduju svo znanje o korištenju memorije i ostalim statistikama kako bi mogli donijeti ispravne odluke. Stoga bismo im trebali vjerovati.

U slučaju problema s memorijom, imamo hrpu postavki koje možemo promijeniti za podešavanje naše aplikacije - počevši od odabira drugog sakupljača smeća, preko postavljanja željenog omjera vremena aplikacije / vremena GC, i na kraju, završavajući postavljanjem fiksnih veličina za segmente memorije.

Postoje i načini za ublažavanje učinaka Full GC-a uzrokovanih eksplicitnim pozivom. Možemo upotrijebiti jednu od zastavica:

-XX: + EksplicitnoGCInvokesConcurrent

ili:

-XX: + EksplicitneGCInvokesConcurrentAndUnloadsClasses

Ako stvarno želimo da naša aplikacija radi ispravno, trebali bismo riješiti stvarni temeljni problem s memorijom.

U sljedećem ćemo poglavlju vidjeti praktični primjer kada izričito pozivate System.gc () čini se korisnim.

4. Primjer upotrebe

4.1. Scenarij

Napišimo testnu aplikaciju. Želimo pronaći situaciju prilikom pozivanja System.gc () moglo biti korisno.

Manje odvoz smeća događa se češće od većeg. Dakle, vjerojatno bismo se trebali usredotočiti na ovo drugo. Pojedini se objekt premješta u prostor s ograničenim prostorom ako je "preživio" nekoliko zbirki i još uvijek je dostupan iz korijena GC-a.

Zamislimo da imamo ogromnu kolekciju predmeta koji su neko vrijeme živi. Zatim, u nekom trenutku, očistit ćemo zbirku predmeta. Možda je dobar trenutak za trčanje System.gc ()?

4.2. Demo aplikacija

Stvorit ćemo jednostavnu konzolu koja će nam omogućiti simulaciju tog scenarija:

javna klasa DemoApplication {privatna statička konačna predmemorija karte = nova HashMap (); public static void main (String [] args) {Skener skenera = novi skener (System.in); while (scanner.hasNext ()) {final String next = scanner.next (); if ("fill" .equals (next)) {for (int i = 0; i <1000000; i ++) {cache.put (randomUUID (). toString (), randomUUID (). toString ()); }} else if ("invalidate" .equals (next)) {cache.clear (); } else if ("gc" .equals (next)) {System.gc (); } else if ("exit" .equals (next)) {System.exit (0); } else {System.out.println ("nepoznato"); }}}}

4.3. Pokretanje demo verzije

Pokrenimo našu aplikaciju s nekoliko dodatnih zastavica:

-XX: + PrintGCDetails -Xloggc: gclog.log -Xms100M -Xmx500M -XX: + UseConcMarkSweepGC

Prve dvije zastavice potrebne su za bilježenje GC informacija. Sljedeće dvije zastavice postavljaju početnu veličinu hrpe, a zatim maksimalnu veličinu hrpe. Želimo držati veličinu hrpe niskom kako bismo prisilili GC da bude aktivniji. Konačno, odlučujemo se za korištenje CMS - istodobnog sakupljača smeća Mark and Sweep. Vrijeme je da pokrenete našu aplikaciju!

Prvo, krenimo pokušajte popuniti prostor za zakup. Tip napuniti.

Možemo istražiti naše gclog.log datoteku da biste vidjeli što se dogodilo. Vidjet ćemo oko 15 kolekcija. Redak zabilježen za pojedinačne zbirke izgleda ovako:

197.057: [GC (neuspjeh dodjele) 197.057: [ParNew: 67498K-> 40K (75840K), 0,0016945 sek.] 168754K-> 101295K (244192K), 0,0017865 sek.] [Times: user = 0,01 sys = 0,00, real = 0,00 secs] sek.]

Kao što vidimo, sjećanje je ispunjeno.

Dalje, idemo sila System.gc () tipkanjem gc. Vidimo da se upotreba memorije nije značajno promijenila:

238.810: [Cijeli GC (System.gc ()) 238.810: [CMS: 101255K-> 101231K (168352K); 0,2634318 sek] 120693K-> 101231K (244192K), [Metaprostor: 32186K-> 32186K (1079296K)], 0,2635908 sek] [Vremena: korisnik = 0,27 sys = 0,00, stvarno = 0,26 sek]

Nakon još nekoliko izvođenja vidjet ćemo da veličina memorije ostaje na istoj razini.

Idemo očistite predmemoriju tipkanjem poništiti. Ne bismo trebali vidjeti da se više linija dnevnika pojavljuju u gclog.log datoteka.

Možemo pokušati napuniti predmemoriju još nekoliko puta, ali GC se ne događa. Ovo je trenutak kada možemo nadmudriti sakupljač smeća. Sada, nakon forsiranja GC, vidjet ćemo redak poput:

262.124: [Cijeli GC (System.gc ()) 262.124: [CMS: 101523K-> 14122K (169324K); 0,0975656 sek.] 103369K-> 14122K (245612K), [Metaprostor: 32203K-> 32203K (1079296K)], 0,0977727 sek.] [Vremena: korisnik = 0,10 sys = 0,00, stvarno = 0,10 sek.]

Izdali smo impresivnu količinu memorije! No, je li to stvarno trebalo sada? Što se dogodilo?

Prema ovom primjeru, pozivanje System.gc () može izgledati primamljivo kada puštamo velike predmete ili poništavamo predmemoriju.

5. Ostale upotrebe

Postoji vrlo malo razloga kada izričiti poziv na System.gc () metoda može biti korisna.

Jedan od mogućih razloga je čišćenje memorije nakon pokretanja poslužitelja - pokrećemo poslužitelj ili aplikaciju koji se uvelike priprema. Nakon toga ima puno predmeta koji se trebaju finalizirati. Međutim, čišćenje nakon takve pripreme ne bi trebalo biti naša odgovornost.

Druga je analiza curenja memorijeto je više praksa otklanjanja pogrešaka nego nešto što bismo željeli zadržati u produkcijskom kodu. Pozivanje System.gc () a to što je prostor hrpe i dalje velik može biti pokazatelj curenja memorije.

6. Sažetak

U ovom smo članku istražili System.gc () metoda i kada bi se mogla činiti korisnom.

Nikada se ne bismo trebali pouzdati u to kada je u pitanju ispravnost naše aplikacije. GC je u većini slučajeva pametniji od nas, a u slučaju bilo kakvih problema s memorijom, trebali bismo razmisliti o podešavanju virtualnog stroja umjesto upućivanja tako eksplicitnog poziva.

Kao i obično, kod korišten u ovom članku možete pronaći na GitHubu.