Java virtuelna mašina (JVM) — obavezna štiva za intervju, verzija za napametovanje🍉
Ukratko o memorij modelu JVM-a
Privatne za nit izvršne podatkovne oblasti: brojač programa, stek Java virtuelne mašine, stek lokalnih metoda.
Deljene izvršne podatkovne oblasti: Java hip, oblast metoda.
Ukratko o brojaču programa
Brojač programa predstavlja indikator broja linije bajtkoda koji trenutna nit izvršava.
Brojač programa ne izaziva StackOverflowError ni OutOfMemoryError.
Ukratko o steku virtuelne mašine
Stek Java virtuelne mašine opisuje memorij model izvršavanja Java metoda. Pri kreiranju niti dodeljuje joj se prostor na steku, a po završetku niti taj prostor se prikuplja.
Elementi na steku služe da podrže virtuelnu mašinu pri pozivu metoda; svaka metoda, prilikom izvršavanja, kreira okvir steka (stack frame) koji čuva tabelu lokalnih promenljivih, stek operanada, dinamičku vezu i povratnu adresu i sl.
Stek virtuelne mašine izaziva dve vrste izuzetaka:
- StackOverflowError: baca se kada nit zahteva dubinu steka veću od one koju virtuelna mašina dozvoljava.
- OutOfMemoryError: baca se ako stek JVM-a može dinamički da se širi, a zauzeće memorije steka premaši ograničenje.
Ukratko o steku lokalnih metoda
Stek lokalnih metoda ima sličnu ulogu kao stek virtuelne mašine, s tim što stek virtuelne mašine služi izvršavanju Java metoda, a stek lokalnih metoda služi lokalnim metodima. Stek virtuelne mašine se može shvatiti kao memorij model za obične Java funkcije, a stek lokalnih metoda kao memorij model za funkcije modifikovane ključnom rečju native.
Stek lokalnih metoda izaziva dve vrste izuzetaka:
- StackOverflowError: baca se kada nit zahteva dubinu steka veću od one koju virtuelna mašina dozvoljava.
- OutOfMemoryError: baca se ako stek JVM-a može dinamički da se širi, a zauzeće memorije steka premaši ograničenje.
Ukratko o hipu u JVM-u
Glavna uloga hipa je čuvanje instanci objekata; u Javi se gotovo sve instance objekata alociraju na hipu, i hip je i najveća oblast u upravljanju memorijom. Java sakupljanje otpada se uglavnom odnosi na ovu oblast — hip. Preko -Xms i -Xmx mogu se podesiti minimalan i maksimalan kapacitet hipa.
Hip baca OutOfMemoryError.
Ukratko o oblasti metoda
Oblast metoda služi za čuvanje podataka o klasi, konstantama, statičkim promenljivama i sl. koje je učitala virtuelna mašina.
Pre JDK6 oblast metoda se realizovala preko trajne generacije (permanent generation), koja lako preliva memoriju. JDK7 iz trajne generacije iznosi bazen konstanti niski, statičke promenljive i sl. JDK8 napušta trajnu generaciju i oblast metoda realizuje meta-prostorom (metaspace) koji se nalazi u lokalnoj memoriji, prebacujući sadržaj trajne generacije iz JDK7 u meta-prostor.
Oblast metoda baca OutOfMemoryError.
Ukratko o bazenu konstanti u vreme izvršavanja
Bazen konstanti u vreme izvršavanja čuva tabelu bazena konstanti, u koju idu razni literali i simboličke reference koje generiše kompajler. Pored simboličkih referenca opisanih u Class fajlu, u njega se smeštaju i direktne reference dobijene prevodom simboličkih referenca. Osim toga, čuva i niske i osnovne tipove.
Pre JDK8 nalazio se u oblasti metoda, pa mu je veličina bila ograničena oblasti metoda. JDK8 smešta bazen konstanti u vreme izvršavanja u hip.
Ukratko o direktnoj memoriji
Direktna memorija se naziva i memorijom van hipa — memorijski objekat se alocira u memorijskoj oblasti van JVM hipa. Njom ne upravlja virtuelna mašina, već operativni sistem. Java nad njom operiše preko DirectByteBuffer-a, čime se izbegava prepisivanje podataka tamo-amo između Java hipa i Native hipa.
Ukratko o procesu kreiranja objekta u Javi
- Proverava se da li parametar te instrukcije može u bazenu konstanti da locira simboličku referencu na klasu, i da li je klasa koju ta referenca predstavlja već učitana, raščlanjena i inicijalizovana; ako nije, najpre se izvršava učitavanje klase.
- Po uspešnoj proveri, virtuelna mašina novom objektu dodeljuje memoriju.
- Po završetku dodele memorije, virtuelna mašina postavlja promenljive članove na nultu vrednost.
- Postavlja se zaglavlje objekta, u koje idu heš-kôd, GC informacije, informacije o brodu, meta-informacije klase kojoj objekat pripada i sl.
- Izvršava se metod init, inicijalizuju se promenljive članovi, izvršava blok koda instanciranja, poziva konstruktor klase, i početna adresa objekta na hipu dodeljuje se referentnoj promenljivoj.
Ukratko o strategiji dodele memorije objektu u JVM-u
- Bump pointer: u memoriji se postavlja jedan pokazivač kao granični indikator, koji iskorišćenu memoriju drži sa jedne, a slobodnu sa druge strane; dodela se vrši pomeranjem pokazivača.
- Slobodna lista (free list): kada memorija Java hipa nije neprekidna, virtuelna mašina mora da održava listu koja beleži ko je memorija dostupna; pri dodeli iz liste se pronalazi dovoljno veliki prostor, dodeljuje objektu i ažurira se zapis liste.
Kako se pri dodeli memorije Java objektu garantuje bezbednost niti
Prvi način: CAS mehanizam uz ponovni pokušaj pri neuspehu, čime se garantuje atomarnost operacije ažuriranja. Ovaj način je manje efikasan.
Drugi način: svaka nit unapred dobije mali komad memorije na Java hipu, pa pri dodeli memorije objektu to obavi neposredno u toj svojoj "privatnoj" memoriji. U pravilu se koristi ova strategija.
Ukratko o memorijskom rasporedu objekta
Raspored čuvanja objekta u memoriji hipa deli se na zaglavlje objekta, podatke instance i poravnanje (padding).
- Zaglavlje objekta uglavnom sadrži dva dela podataka: MarkWord i pokazivač tipa.
MarkWord služi za čuvanje heš-koda (HashCode), uzrasta GC generacije, bita oznake stanja broda, broda koji drži nit, ID pristrasne niti i sl.
Pokazivač tipa jeste pokazivač kojim objekat ukazuje na metapodatke svoje klase; ako je objekat Java niz, postojiće i deo koji beleži dužinu niza.
Podaci instance čuvaju informacije o poljima različitih tipova definisanim u kôdu.
Poravnanje služi kao mesto za popunu. Virtuelna mašina HotSpot zahteva da početna adresa objekta bude višekratnik broja 8, pa je potrebno poravnanje.
Kako se utvrđuje da li je objekat otpad
- Metod brojača referenci:
Postavlja se brojač referenci: kad se objekat referencira, brojač se uveća za 1, a kad referenca prestane da važi, smanji se za 1; ako je brojač 0, objekat se označava kao otpad. Postoji problem kružnih referenci između objekata, pa se ovaj metod uglavnom ne koristi.
- Analiza dostižnosti:
Polazeći od korenskog objekta GC Roots kao početnog čvora, prema odnosu referenci pretražuje se nadole; ako neki objekat ne bude pronađen, označava se kao otpad. Objekti koji mogu biti GC Roots obuhvataju objekte referencirane u steku virtuelne mašine i steku lokalnih metoda, objekte referencirane statičkim svojstvima klase i objekte referencirane konstantama.
Ukratko o tipovima referenci u Javi
- Jaka referenca: objekat na koji ukazuje jaka referenca neće biti prikupljen. Jaka referenca se obično kreira operatorom new.
- Meka referenca: objekat na koji ukazuje meka referenca prikuplja se tek kada ponestane memorije. Meka referenca se obično kreira klasom SoftReference.
- Slaba referenca: sakupljač otpada je prikuplja čim je sretne, što znači da živi samo do sledećeg sakupljanja otpada. Slaba referenca se obično kreira klasom WeakReference.
- Fantomska referenca (phantom reference): preko nje se ne može dobiti objekat. Njena jedina svrha jeste da se prilikom prikupljanja objekta dobije sistemsko obaveštenje. Fantomska referenca mora da se koristi zajedno sa redom referenci.
Ukratko o algoritmima mark-sweep, mark-compact i mark-copy
- Mark-sweep: prvo se označe objekti koje treba ukloniti, a zatim se prikupljaju u jednom potezu. Ovaj metod nije naročito efikasan i stvara mnogo nepovezanih fragmenata.
- Mark-compact: prvo se označe živi objekti, zatim se svi živi objekti pomeraju ka jednom kraju, a potom se oslobađa memorija s one strane granice tog kraja.
- Mark-copy: dostupna memorija se po kapacitetu podeli na dva jednaka dela, i svaki put se koristi samo jedan. Kada se taj deo potroši, živi objekti se kopiraju u drugi deo, a ranije iskorišćen memorijski prostor se u jednom potezu oslobađa.
Ukratko o generacijskom algoritmu sakupljanja
Prema ciklusu preživljavanja objekata memorija se deli na nekoliko oblasti, i za različite oblasti se koriste odgovarajući algoritmi sakupljanja.
Uobičajeno se hip deli na mladu generaciju i staru generaciju, i za te dve oblasti se koriste različiti algoritmi.
Mlada generacija koristi: mark-copy algoritam.
Stara generacija koristi: mark-sweep ili mark-compact algoritam.
Ukratko o sakupljaču otpada Serial
Serial je jednonitni, serijski sakupljač otpada. Pri sakupljanju otpada mora da pauzira sve ostale niti. Mlada generacija koristi mark-copy algoritam, a stara generacija mark-compact algoritam. Jednostavan je i efikasan.
Ukratko o sakupljaču otpada ParNew
Sakupljač otpada ParNew se može smatrati višenitnom verzijom sakupljača Serial; mlada generacija koristi mark-copy algoritam, a stara generacija mark-compact algoritam.
Ukratko o sakupljaču otpada Parallel Scavenge
Fokusira se na propusnost (throughput), odnosno odnos vremena rada CPU-a na kôd i ukupnog vremena CPU-a (vreme rada na kôd + vreme sakupljanja otpada). Mlada generacija koristi mark-copy algoritam, a stara generacija mark-compact algoritam.
Ukratko o sakupljaču otpada CMS
Sakupljač otpada CMS fokusira se na najkraće vreme pauze. CMS je najranije predloženi konkurentni sakupljač; nit sakupljanja otpada radi istovremeno sa nitima korisnika. Koristi mark-sweep algoritam. Ovaj sakupljač se deli na sledeće korake: inicijalno označavanje, konkurentno označavanje, konkurentno prečišćavanje, konkurentno uklanjanje i konkurentno resetovanje.
- Inicijalno označavanje: pauziraju se ostale niti (stop the world) i označe se objekti direktno povezani sa GC roots.
- Konkurentno označavanje: proces analize dostižnosti (program ne pauzira).
- Konkurentno prečišćavanje: pronalaze se objekti koji su u fazi konkurentnog označavanja napredovali iz mlade u staru generaciju, ponovo se označavaju, a virtuelna mašina se pauzira (stop the world) i skeniraju se preostali objekti u CMS hipu.
- Konkurentno uklanjanje: uklanjaju se otpadni objekti (program ne pauzira).
- Konkurentno resetovanje: resetuju se strukture podataka CMS sakupljača.
Ukratko o sakupljaču otpada G1
Za razliku od Serial, Parallel Scavenge i CMS, sakupljač otpada G1 hip deli na više međusobno jednakih nezavisnih oblasti (Region), pa mlada i stara generacija više nisu fizički odvojene. Uvođenjem pojma Region, prvobitno jedinstven memorijski prostor se deli na više malih prostora, tako da svaki mali prostor može nezavisno da obavi sakupljanje otpada.
- Inicijalno označavanje: označe se objekti direktno povezani sa GC roots.
- Konkurentno označavanje: analiza dostižnosti.
- Konačno označavanje: objekti koje je nit korisnika izmenila tokom konkurentnog označavanja ponovo se označe.
- Selektivno prikupljanje: za svaki Region se poredi vrednost prikupljanja i trošak, a zatim se, na osnovu GC vremena pauze koje korisnik želi, sastavlja plan prikupljanja i obavlja prikupljanje.
Ukratko o Minor GC
Minor GC je sakupljanje otpada koje se dešava u mladoj generaciji; pošto Java objekti uglavnom kratko žive, Minor GC je veoma čest, a brzina prikupljanja je obično i velika.
Ukratko o Full GC
Full GC čisti ceo prostor hipa — uključujući mladu generaciju i trajnu generaciju. Poziv System.gc(), nedovoljno prostora u staroj generaciji, neuspeh jemstva za dodelu prostora i nedovoljno prostora u trajnoj generaciji mogu izazvati Full GC.
Uobičajene strategije dodele memorije
U većini slučajeva objekti se dodeljuju u oblasti Eden mlade generacije; kada Eden nema dovoljno prostora, pokreće se jedan Minor GC.
Veliki objekti zahtevaju mnogo neprekidnog memorijskog prostora, pa idu direktno u dodelu u staroj generaciji.
Ako objekat preživi prvi Minor GC i Survivor ga može primiti, biće premešten u Survivor sa uzrastom 1, i posle svakog preživljenog Minor GC uzrast mu se uveća za 1; kada naraste do određenog nivoa (podrazumevano 15), biva unapređen u staru generaciju.
Ako u Survivor-u zbir veličina svih objekata istog uzrasta premaši polovinu Survivor-a, objekti uzrasta ne manjeg od tog mogu ići direktno u staru generaciju.
Pre Minor GC, virtuelna mašina mora da proveri da li je najveći raspoloživi neprekidni prostor stare generacije veći od ukupnog prostora objekata mlade generacije; ako jeste, taj Minor GC je sigurno bezbedan. Ako nije, JVM proverava da li parametar HandlePromotionFailure dozvoljava neuspeh jemstva; ako dozvoljava, nastavlja da proverava da li je najveći raspoloživi neprekidni prostor stare generacije veći od prosečne veličine objekata koji su prošlih puta napredovali u staru generaciju — ako jeste, obavlja se Minor GC, a inače se pretvara u jedan Full GC.
Ukratko o procesu učitavanja klase u JVM-u
- Učitavanje:
- Pribavlja se binarni tok bajtova klase preko punog imena klase.
- Statička struktura za čuvanje klase pretvara se u izvršnu strukturu podataka oblasti metoda.
- U memoriji se generiše Class objekat klase, kao ulaz u podatke oblasti metoda.
Provera: proverava se ispravnost formata fajla, metapodataka, bajtkoda, simboličkih referenci i sl.
Priprema: u oblasti metoda se promenljivim klase dodeljuje memorija i postavljaju se na nultu vrednost.
Raščlanjivanje: simboličke reference se pretvaraju u direktne reference.
Inicijalizacija: izvršava se metod klase konstruktor clinit, čime se vrši stvarna inicijalizacija.
Ukratko o učitavačima klasa u JVM-u
- BootstrapClassLoader (učitavač pri pokretanju): učitava jar pakete i klase ispod /lib. Pisan u C++-u.
- ExtensionClassLoader (učitavač proširenja): jar paketi i klase iz direktorijuma /lib/ext. Pisan u Javi.
- AppClassLoader (aplikativni učitavač): učitava jar pakete i klase sa trenutnog classPath-a. Pisan u Javi.
Ukratko o mehanizmu delegiranja roditelju
Kada učitavač klase dobije zahtev za učitavanje klase, najpre proverava da li je ta klasa već učitana. Već učitana klasa se odmah vraća; ako nije učitana, zahtev za učitavanje se najpre prosleđuje roditeljskom učitavaču, i tako redom do učitavača pri pokretanju; tek kada roditeljski učitavač ne može da završi posao, pokušava sam da učita.
Redosled učitavanja klasa: BootstrapClassLoader -> ExtensionClassLoader -> AppClassLoader -> CustomClassLoader; redosled provere da li je klasa učitana: CustomClassLoader -> AppClassLoader -> ExtensionClassLoader -> BootstrapClassLoader.
Prednosti mehanizma delegiranja roditelju
- Izbegava se ponovljeno učitavanje klasa. Ista klasa učitana različitim učitavačima dala bi različite klase; delegiranje roditelju garantuje stabilan rad Java programa.
- Garantuje da se jezgro API ne izmeni.
- Kako razbiti mehanizam delegiranja roditelju:
- preopteretiti metod loadClass(), odnosno definisati sopstveni učitavač klase.
Kako izgraditi sopstveni učitavač klase
Napravi se nova klasa koja nasleđuje java.lang.ClassLoader i prepisuju se metode findClass, loadClass, defineClass.
Uobičajeni parametri za štelovanje JVM-a
- -Xms: početna veličina hipa.
- -Xmx: maksimalna veličina hipa.
- -XX:NewSize: veličina mlade generacije.
- -XX:MaxNewSize: maksimalna vrednost mlade generacije.
- -XX:PermSize: početna vrednost trajne generacije.
- -XX:MaxPermSize: maksimalna vrednost trajne generacije.
- -XX:NewRatio: odnos mlade i stare generacije.
Autor priloga: Houduan Jishu Xiaoniu Shuo Izvorni link: https://mp.weixin.qq.com/s/PmeH38qWVxyIhBpsAsjG7w
