Memóriakezelés Java interjúkérdésekben (+ válaszok)

Ez a cikk egy sorozat része: • Java Collections interjúkérdések

• Java típusú rendszerinterjúkérdések

• Java egyidejű interjúkérdések (+ válaszok)

• Java osztály felépítése és inicializálása interjúkérdések

• Java 8 interjúkérdések (+ válaszok)

• Memóriakezelés a Java interjúkérdésekben (+ válaszok) (aktuális cikk) • Java Generics interjúkérdések (+ válaszok)

• Java Flow Control interjúkérdések (+ válaszok)

• Java kivételek interjúkérdések (+ válaszok)

• Java Annotations Interjúkérdések (+ Válaszok)

• A tavaszi keretrendszer legfontosabb interjúi

1. Bemutatkozás

Ebben a cikkben néhány memóriakezelési kérdést tárunk fel, amelyek gyakran felmerülnek a Java fejlesztői interjúk során. A memóriakezelés olyan terület, amelyet nem olyan sok fejlesztő ismer.

Valójában a fejlesztőknek általában nem kell közvetlenül foglalkozniuk ezzel a koncepcióval - mivel a JVM gondoskodik az apró részletekről. Hacsak valami nem megy komolyan, még a tapasztalt fejlesztőknek sincs elérhető kezükben pontos információ a memóriakezelésről.

Másrészt ezek a fogalmak valójában meglehetősen elterjedtek az interjúkban - ugorjunk hát bele.

2. Kérdések

Q1. Mit jelent a „Javában kezelt memória” állítás?

A memória az a kulcsfontosságú erőforrás, amelyre az alkalmazásnak szüksége van a hatékony futtatáshoz, és mint minden erőforrás, ez is kevés. Mint ilyen, az alkalmazáshoz vagy az alkalmazás különböző részeihez történő elosztása és elosztása sok odafigyelést és megfontolást igényel.

A Java-ban azonban a fejlesztőnek nem kell kifejezetten lefoglalnia és elosztania a memóriát - a JVM-nek és pontosabban a Garbage Collector-nak kötelessége kezelni a memóriát, így a fejlesztőnek nem kell.

Ez ellentétes azokkal a nyelvekkel, mint például a C, ahol egy programozó közvetlen hozzáféréssel rendelkezik a memóriához, és szó szerint hivatkozik a memóriacellákra a kódjában, ami sok teret enged a memóriaszivárgásoknak.

Q2. Mi a szemétgyűjtés és milyen előnyei vannak?

A szemétgyűjtés a halom memória áttekintése, a használatban lévő és a nem használt objektumok azonosítása, valamint a fel nem használt objektumok törlése.

Használatban lévő objektum vagy hivatkozott objektum azt jelenti, hogy a program bizonyos része továbbra is fenntart egy mutatót az adott objektumra. Egy nem használt objektumra vagy hivatkozatlan objektumra a program egyetlen része sem hivatkozik tovább. Tehát a hivatkozás nélküli objektum által használt memória visszanyerhető.

A szemétgyűjtés legnagyobb előnye, hogy eltávolítja rólunk a kézi memória-elosztás / terjesztés terheit, így a kéznél lévő probléma megoldására koncentrálhatunk.

Q3. Van-e hátránya a szemétszállításnak?

Igen. A szemétgyűjtő bármikor hatással van az alkalmazás teljesítményére. Ennek oka, hogy az alkalmazás összes többi szálát le kell állítani, hogy a szemétszedő szál hatékonyan végezhesse munkáját.

Az alkalmazás követelményeitől függően ez valós probléma lehet, amelyet az ügyfél nem fogad el. Ez a probléma azonban ügyesen optimalizálható és szemétszedő hangolással, valamint különböző GC algoritmusok használatával nagymértékben csökkenthető vagy akár kiküszöbölhető.

Q4. Mit jelent a „Stop-The-World” kifejezés?

Amikor a szemétgyűjtő szál fut, akkor más szálak leállnak, vagyis az alkalmazás pillanatnyilag leáll. Ez analóg a ház takarításával vagy a füstöléssel, ahol az utasoknak a folyamat befejezéséig megtagadják a hozzáférést.

Egy alkalmazás igényeitől függően a „stop the world” szemétszállítás elfogadhatatlan fagyást okozhat. Ezért fontos a szemétgyűjtő hangolása és a JVM optimalizálása, hogy a fagyás legalább elfogadható legyen.

Q5. Mi a verem és a halom? Mit tárolnak ezek a memóriastruktúrák, és hogyan kapcsolódnak egymáshoz?

A verem a memória egy része, amely információkat tartalmaz a beágyazott módszerhívásokról a program aktuális pozíciójáig. Tartalmazza az összes helyi változót és hivatkozásokat a halom objektumaira, amelyeket a jelenleg végrehajtó módszerek definiálnak.

Ez a struktúra lehetővé teszi, hogy a futásidő visszatérjen a metódusból, ismerve a címet, ahonnan hívták, és a metódusból való kilépés után törölheti az összes helyi változót is. Minden szálnak megvan a maga vereme.

A kupac egy nagy memória, amelyet objektumok kiosztására szánnak. Amikor létrehoz egy objektumot a új kulcsszó, a halomra lesz hozzárendelve. Az objektumra való hivatkozás azonban a veremben él.

Q6. Mi a generációs szemétgyűjtés és mitől válik népszerű szemétgyűjtési megközelítéssé?

A generációs szemétgyűjtés lazán meghatározható a szemétgyűjtő által alkalmazott stratégiaként, ahol a kupacot több generációnak nevezett szakaszra osztják fel, amelyek mindegyike objektumokat fog tartani a halom „korának” megfelelően.

Amikor a szemétgyűjtő fut, a folyamat első lépését jelölésnek nevezzük. Itt azonosítja a szemétgyűjtő, hogy mely memóriadarabok vannak használatban, és melyek nem. Ez nagyon időigényes folyamat lehet, ha a rendszer összes objektumát be kell vizsgálni.

Amint egyre több objektumot osztanak ki, az objektumok listája növekszik és növekszik, ami egyre hosszabb szemétgyűjtési időt eredményez. Az alkalmazások empirikus elemzése azonban azt mutatta, hogy a legtöbb objektum rövid életű.

A generációs szemétszállítással az objektumokat „életkoruk” szerint csoportosítják abból a szempontból, hogy hány szemétgyűjtési ciklust éltek túl. Így a munka zöme különböző kisebb és nagyobb gyűjtési ciklusokra terjedt el.

Ma szinte minden szemétszedő generációs. Ez a stratégia annyira népszerű, mert az idő múlásával az optimális megoldásnak bizonyult.

Q7. Írja le részletesen, hogyan működik a generációs szemétgyűjtés

Ahhoz, hogy megfelelően megértsük, hogyan működik a generációs szemétszállítás, először is fontos emlékezzen a Java halom felépítésére a generációs szemétszállítás megkönnyítése érdekében.

A kupac kisebb terekre vagy generációkra oszlik. Ezek a terek a Young Generation, az Old or Tenured Generation és az állandó Generation.

A a fiatal generáció otthont ad az újonnan létrehozott tárgyaknak. A legtöbb alkalmazás empirikus vizsgálata azt mutatja, hogy az objektumok többsége gyorsan rövid életű, ezért hamarosan jogosult lesz gyűjtésre. Ezért az új tárgyak itt kezdik útjukat, és csak azután „léptetik elő őket” a régi generációs térbe, miután elérik egy bizonyos „kort”.

A kifejezés "kor" generációs szemétszállításban az objektum fennmaradt gyűjtési ciklusainak számára utal.

A fiatal generációs tér további három térre oszlik: egy Eden-térre és két túlélőtérre, például Survivor 1 (s1) és Survivor 2 (s2).

A a régi generáció olyan objektumoknak ad otthonthosszabb ideig éltek az emlékezetben, mint egy bizonyos „életkor”. A fiatal nemzedék szemétszállítását túlélő tárgyakat erre a helyre emelik. Általában nagyobb, mint a fiatal generáció. Mivel nagyobb a mérete, a szemétszállítás drágább és ritkábban fordul elő, mint a fiatal generációnál.

A állandó nemzedékvagy gyakrabban hívják, PermGen, a JVM által megkövetelt metaadatokat tartalmazza az alkalmazásban alkalmazott osztályok és módszerek leírására. Tartalmazza az internált húrok tárolására szolgáló karakterkészletet is. Futás közben a JVM tölti be, az alkalmazás által használt osztályok alapján. Ezenkívül a platformkönyvtári osztályok és módszerek itt tárolhatók.

Első, minden új objektumot az Eden térhez rendelnek. Mindkét túlélőtér üresen indul. Amikor az Eden tér megtelik, kisebb szemétgyűjtés indul. A hivatkozott objektumok az első túlélőtérbe kerülnek. A hivatkozás nélküli objektumok törlődnek.

A következő kisebb GC során ugyanez történik az Eden térrel is. A hivatkozás nélküli objektumokat törli, és a hivatkozott objektumokat egy túlélő helyre helyezi át. Ebben az esetben azonban átkerülnek a második túlélő térbe (S2).

Ezenkívül az első túlélő térben (S1) lévő utolsó kisebb GC-ből származó tárgyak életkora növekszik, és átkerül az S2-be. Miután az összes fennmaradt tárgyat áthelyezték az S2-be, az S1 és az Eden tér is megtisztul. Ezen a ponton az S2 különböző korú objektumokat tartalmaz.

A következő kisebb GC-n ugyanaz a folyamat megismétlődik. Ezúttal azonban a túlélő terek váltanak. A hivatkozott objektumokat az Eden és az S2 egyaránt áthelyezik az S1-be. A túlélő tárgyakat öregítik. Eden és S2 törlődik.

Minden kisebb szemétgyűjtési ciklus után minden objektum korát ellenőrzik. Azok, akik elérték egy bizonyos önkényes kort, például 8, a fiatal generációból az öreg vagy a tenorált nemzedékbe kerülnek. Minden további kisebb GC ciklus esetében az objektumok továbbra is a régi generációs térbe kerülnek.

Ez nagyjából kimeríti a szemétszállítás folyamatát a fiatal generációban. Végül egy nagy szemétgyűjtést hajtanak végre a régi generáción, amely megtisztítja és tömöríti azt a helyet. Minden nagyobb GC esetében több kisebb GC létezik.

Q8. Mikor válik egy objektum jogosulttá a szemétszállításhoz? Írja le, hogyan gyűjt a Gc egy jogosult objektumot?

Az objektum akkor válik jogosulttá a Szemétgyűjtésbe vagy a GC-be, ha nem érhető el élő szálakból vagy statikus hivatkozásokból.

A legegyszerűbb eset, amikor egy objektum jogosulttá válik a szemétszállításra, ha minden hivatkozása érvénytelen. Az élő külső referencia nélküli ciklikus függőségek szintén alkalmasak a GC-re. Tehát, ha az A objektum hivatkozik a B objektumra és a B objektum hivatkozik az A objektumra, és nincs más élő referenciájuk, akkor mind az A, mind a B objektum jogosult lesz a szemétgyűjtésre.

Egy másik nyilvánvaló eset, amikor a szülő objektum nullára van állítva. Amikor egy konyhai tárgy belsőleg hivatkozik egy hűtőszekrényre és egy mosogatóra, és a konyhai tárgy nullára van állítva, akkor a hűtőszekrény és a mosogató is jogosult lesz szemétszedésre a szülők, a konyha mellett.

Q9. Hogyan indíthatja el a szemétszedést a Java kódból?

Ön, mint Java programozó, nem kényszerítheti a Java szemétgyűjtését; csak akkor vált ki, ha a JVM úgy gondolja, hogy a Java halom méretén alapuló szemétgyűjtésre van szüksége.

Mielőtt eltávolítana egy objektumot a memóriából, a szemétgyűjtő szál meghívja az objektum finalize () metódusát, és lehetőséget ad bármilyen szükséges tisztítás elvégzésére. Meghívhatja ezt a módszert egy objektumkódra is, azonban nincs garancia arra, hogy szemétgyűjtés történik, amikor meghívja ezt a módszert.

Ezen kívül vannak olyan módszerek, mint a System.gc () és a Runtime.gc (), amelyek a szemétgyűjtés iránti kérelem elküldésére szolgálnak a JVM számára, de nem garantált, hogy a szemétszállítás megtörténik.

Q10. Mi történik, ha nincs elég halom hely az új objektumok tárolására?

Ha nincs szabad hely új objektum létrehozásához a Heap-ban, a Java Virtual Machine dob OutOfMemoryError vagy pontosabbanjava.lang.OutOfMemoryError halom helyet.

Q11. Lehetséges "feltámasztani" egy olyan tárgyat, amely jogosulttá vált a szemétszállításra?

Amikor egy objektum jogosulttá válik a szemétszállításra, a GC-nek futtatnia kell a véglegesíteni módszer rajta. A véglegesíteni A metódus garantáltan csak egyszer fog futni, így a GC megjelöli az objektumot véglegesítettként, és pihenőt ad a következő ciklusig.

Ban,-ben véglegesíteni metódus segítségével technikailag „feltámaszthat” egy objektumot, például hozzárendelve a statikus terület. Az objektum újra életre kelne, és nem lenne jogosult a szemétszállításra, ezért a GC a következő ciklus alatt nem gyűjtené be.

Az objektumot azonban befejezettként jelölnék meg, így amikor újra jogosulttá válik, a véglegesítési módszert nem hívják meg. Lényegében ezt a „feltámadási” trükköt csak egyszer fordíthatja az objektum élettartama alatt. Vigyázzon, hogy ezt a csúnya csapkodást csak akkor szabad használni, ha valóban tudja, mit csinál - ennek a trükknek a megértése azonban némi betekintést nyújt a GC működésébe.

Q12. Ismertesse az erős, gyenge, puha és fantom hivatkozásokat és szerepüket a szemétszállításban.

Bármennyire is kezelik a memóriát a Java-ban, a mérnököknek a lehető legtöbb optimalizálást kell végrehajtaniuk a késés minimalizálása és az átviteli sebesség maximalizálása érdekében a kritikus alkalmazásokban. Annyira, mint lehetetlen kifejezetten ellenőrizni, ha a szemétszállítás elindul a JVM-ben, befolyásolhatjuk, hogyan történik ez az általunk létrehozott objektumok tekintetében.

A Java referenciaobjektumokkal látja el az általunk létrehozott objektumok és a szemétgyűjtő kapcsolatát.

Alapértelmezés szerint minden objektumra, amelyet egy Java programban létrehozunk, erősen hivatkozik egy változó:

StringBuilder sb = új StringBuilder ();

A fenti részletben a új kulcsszó létrehoz egy új StringBuilder tárgyat, és a kupacon tárolja. A változó sb majd tárolja a erős referencia ehhez a tárgyhoz. A szemétgyűjtő számára ez azt jelenti, hogy az adott StringBuilder Az objektum egyáltalán nem jogosult gyűjtésre, mert a felhasználó erős hivatkozással rendelkezik rá sb. A történet csak akkor változik, ha semmissé tesszük sb mint ez:

sb = null;

A fenti vonal felhívása után az objektum jogosult lesz a gyűjtésre.

Megváltoztathatjuk ezt a kapcsolatot az objektum és a szemétgyűjtő között, ha kifejezetten becsomagoljuk egy másik referenciaobjektumba, amely belül található java.lang.ref csomag.

A puha referencia a fenti objektumra így hozható létre:

StringBuilder sb = új StringBuilder (); SoftReference sbRef = új SoftReference (sb); sb = null;

A fenti részletben két hivatkozást hoztunk létre a StringBuilder tárgy. Az első sor létrehozza a erős referenciasb a második sor pedig a puha referenciasbRef. A harmadik sornak lehetővé kell tennie az objektum begyűjtését, de a szemétszedő elhalasztja a gyűjtését sbRef.

A történet csak akkor változik meg, ha a memória szűkös lesz, és a JVM az an dobásának szélén áll Elfogyott a memória hiba. Más szavakkal, a csak puha referenciákkal rendelkező tárgyakat a memória helyreállításának utolsó lehetőségeként gyűjtik össze.

A gyenge referencia segítségével hasonló módon hozható létre WeakReference osztály. Mikor sb értéke null, és a StringBuilder Az objektumnak csak gyenge referenciája van, a JVM szemétgyűjtője semmilyen kompromisszumot nem fog kötni, és a következő ciklusban azonnal összegyűjti az objektumot.

A fantom hivatkozás hasonló egy gyenge referenciához, és csak fantom hivatkozásokat tartalmazó objektum várakozás nélkül gyűlik össze. A fantom hivatkozások azonban azonnal megjelennek, amint tárgyaikat összegyűjtik. Megkérdezhetjük a referencia várólistát, hogy pontosan tudjuk, mikor gyűjtöttük össze az objektumot.

Q13. Tegyük fel, hogy van kör alakú referenciánk (két objektum, amelyek egymásra hivatkoznak). Lehet, hogy egy ilyen tárgypár alkalmas a szemétszállításra, és miért?

Igen, pár kör alakú referenciával rendelkező objektum jogosulttá válhat a szemétszállításra. Ez annak köszönhető, hogy a Java szemétgyűjtője hogyan kezeli a körkörös hivatkozásokat. Az objektumokat nem akkor tekinti élőnek, ha bármiféle utalás van rájuk, hanem akkor, amikor elérhetőek az objektumgrafikon navigálásával, néhány szemétszedési gyökérből kiindulva (egy élő szál helyi változója vagy egy statikus mező). Ha egy kör alakú referenciájú objektumpár egyetlen gyökérből sem érhető el, akkor jogosultnak tekinthető a szemétszállításra.

Q14. Hogyan jelennek meg a húrok az emlékezetben?

A Húr a Java példány két mezővel rendelkező objektum: a char [] értéke mező és egy int hash terület. A érték mező a karakterlánc, amely magát a karakterláncot képviseli, és a hash mező a hash kód egy stringet, amelyet nullával inicializálunk, az első során kiszámítva hash kód() hívás és azóta gyorsítótár. Kíváncsi éles esetként, ha a hash kód egy karakterlánc értéke nulla, azt minden alkalommal újra kell számolni hash kód() nak, nek hívják.

Fontos dolog, hogy a Húr a példány változhatatlan: nem tudja megszerezni vagy módosítani az alapul szolgáló elemet char [] sor. A húrok másik jellemzője, hogy a statikus konstans húrok egy húrkészletben vannak betöltve és gyorsítótárban. Ha több egyforma van Húr a forráskódban található objektumokat, futás közben mindet egyetlen példány képviseli.

Q15. Mi az a húrépítő és milyen esetei vannak? Mi a különbség a húr hozzáadása egy húrépítőhöz és a két húr összekapcsolása + operátorral? Hogyan különbözik a Stringbuilder a Stringbuffertől?

StringBuilder lehetővé teszi a karakterláncok manipulálását karakterek és karakterláncok hozzáfűzésével, törlésével és beszúrásával. Ez egy módosítható adatszerkezet, szemben a Húr osztály, amely megváltoztathatatlan.

Kettő összefűzésénél Húr példányok esetén új objektum jön létre, és a karakterláncok átmásolásra kerülnek. Ez hatalmas szemétszedőt hozhat a feje fölé, ha hurkon belül húrot kell létrehoznunk vagy módosítanunk. StringBuilder sokkal hatékonyabban teszi lehetővé a karakterlánc-manipulációk kezelését.

StringBuffer eltér a StringBuilder annyiban biztonságos. Ha egy szálat egyetlen szálban kell kezelnie, használja a StringBuilder helyette.

3. Következtetés

Ebben a cikkben bemutattuk a Java mérnöki interjúkban gyakran megjelenő leggyakoribb kérdéseket. A memóriakezeléssel kapcsolatos kérdéseket többnyire a Senior Java fejlesztői jelölteknek teszik fel, mivel a kérdező arra számít, hogy nem triviális alkalmazásokat épített fel, amelyeket sokszor memóriaproblémák sújtanak.

Ezt nem a kérdések kimerítő felsorolásaként kell kezelni, hanem a további kutatások indítópultjaként. Mi, a Baeldungnál sikert kívánunk a közelgő interjúkhoz.

Következő » Java Generics interjúkérdések (+ válaszok) « Korábbi Java 8 interjúkérdések (+ válaszok)