A Java 6 nyelvi elemekben nem hozott akkora ugrást, mint a Java 5, de jelentősen bővült a felhasználható eszközök száma, sok új osztály került a java.util csomagba, ilyen a Deque osztály is, amelynek a neve "Double Ended QUEue" kifejezésből származik. Ez azt jelenti, hogy a Deque példányt használhatjuk queue és stack üzemmódban is. Csapjunk a közepébe, és nézzük az alábbi programot:
import java.util.ArrayDeque; import java.util.Deque; public class DequeTest { public static void main(String [] args) { Deque deque = new ArrayDeque(); deque.add("Első"); deque.add("Második"); deque.add("Középső"); deque.add("Utolsó előtti"); deque.add("Utolsó"); System.out.println(deque.toString()); /** * Kiolvassuk az elemet, de nem vesszük ki */ System.out.println(deque.peekFirst()); System.out.println(deque.peekLast()); /** * Kiolvassuk az elemet, és el is távolítjuk */ System.out.println(deque.pollFirst()); System.out.println(deque.pollLast()); /** * Visszatesszük a kivett elemeket */ deque.push("Első"); deque.add("Utolsó"); System.out.println(deque.toString()); } }
Ahogy a programból látszik: egyszerűen hozzá tudunk adni elemeket a listához, amelynek elejéről és végéről is közel azonos módon tudunk elemeket kiolvasni, kivenni illetve beletenni. A program kimenete:
[Első, Második, Középső, Utolsó előtti, Utolsó] Első Utolsó Első Utolsó [Első, Második, Középső, Utolsó előtti, Utolsó]
Az osztály több metódussal is rendelkezik a fentieken kívül, amelyek alapvetően rövidebb nevet jelentenek a gyakran használt műveletekhez, másrészt pedig a metódusok egy csoportjánál NoSuchElementException váltódik ki, ha üres a lista.
A Java 6 megjelenésével több belső információhoz férhetünk hozzá futásidőben, amelyek közül érdekes lehet lekérdezni a szálak adatait, beleértve olyan metrikát, mint a felhasznált CPU idő. A titok a ManagementFactory statikus metódusai körül van, innen tudjuk lekérdezni a ThreadMXBean példányt, amelyből le tudjuk kérdezni az éppen létező szálak adatait:
ThreadMXBean threads = ManagementFactory.getThreadMXBean(); ThreadInfo[] threadInfos = threads.getThreadInfo(threads.getAllThreadIds()); for (int i = 0; i < threadInfos.length; i++) { long nanoTime = threads.getThreadCpuTime(threadInfos[i].getThreadId()); System.out.println("cpuTime[" + threadInfos[i].getThreadName() + "]: " + nanoTime); }
A CPU időn túl több metrika is felderíthető, érdemes megtekinteni a ThreadMXBean illetve a ThreadInfo metódusait.
A java egy régi (1.2-es verziója óta létező) de kevéssé ismert részéhez tartozik a gyenge referencia. Igaz alkalmazási területe is elég szűk, de ha tudjuk, hogy léteznek és mire jók, a megfelelő időben előhúzva a zsebünkből nagyon hasznos tud lenni a java.lang.ref
csomag. Ha egy mondatban össze kellene foglalni mi is az a gyenge referencia akkor úgy írnám le, hogy olyan hivatkozás egy objektumra, ami nem akadályozza meg a szemétgyűjtőt (garbage collector), hogy a hivatkozott objektumot kitakarítsa a memóriából. Ahhoz, hogy pontosan megértsük hogy miről is van szó, egy kicsit elő kell vennünk a szemétgyűjtésről tanultakat.
Az alapoknál kezdve, a java nyelvben nem kell foglalkoznunk az objektumok memóriaterületének felszabadításával, a JVM szemétgyűjtője elvégzi ezt helyettünk minden olyan objektumra, ami már nem elérhető az aktuális programfutási szakaszból. Egész pontosan erős referenciával nem elérhető. Nézzünk erős referenciára egy példát:
String süti = new String("kókuszkocka");
Itt a süti egy erős referencia egy String objektumra. Ha a süti kikerül a láthatósági körünkből, vagy null értéket adunk neki (most nem vagyunk éhesek), akkor a szemétgyűjtő bármikor begyűjtheti azt. Fontos azonban, hogy a szemétgyűjtő lusta és nem azonnal szabadítja fel a memóriát, lehet hogy csak akkor szabadítja fel, amikor teljesen elfogy a JVM rendelkezésére álló memória (megtelik a hűtő). Próbáljuk meg visszaszerezni a sütinket, amíg nem késő...
A java.lang.ref csomag háromféle referenciát ad a kezünkbe, ezek erősségi sorrendben a következők:
- puha (soft)
- gyenge (weak)
- fantom (phantom).
Gyenge referencia
Nézzük meg először a gyenge referenciát. Ha ezzel a referenciával hivatkozunk egy objektumra, és az erős referenciákon keresztül már nem elérhető, akkor azt a szemétgyűjtő bármikor megsemmisítheti, de addig amíg ez meg nem történik mi is elérhetjük, és visszaállíthatjuk. Nézzük meg példával:
WeakReference<String> sütiemlék = new WeakReference<String>(süti); süti = null;
Ekkor - amíg a szemétgyűjtő ki nem söpri a memóriát - a sütiemlék.get() utasítással még elérhetjük a sütinket. Természetesen ekkor beállíthatunk egy (erős) referenciát a sütire, így kiragadva a sütit a GC kezéből. Ha a sütink már megsemmisült null értéket kapunk vissza a get() hívásra:
süti = sütiemlék.get(); if (süti!=null) System.out.println(süti); else System.out.println("valaki már megette");
Szép-szép, de a valós életben mire jó ez?
- Gyorstárak létrehozására: már betöltött de ideiglenes nem kellő nagy méretű fájlokat (pl. képeket) újra előhozhatunk a memóriából, és nem kell újra felolvasnunk azokat a lemezről.
- Olyan osztályokhoz amiket nem tudunk kiterjeszteni, pl. egy factory-tól kapott, egy adott interfészt megvalósító osztályokhoz rendelhetünk további attribútumokat, úgy hogy ezzel nem tartjuk bent az eredeti objektumokat a memóriában. Ehhez ad további segítséget a weakHashMap amit később tárgyalunk.
Puha referencia
A puha (Soft) referencia hasonlóan működik mint a gyenge referencia, a különbségük mindössze abban rejlik, hogy a puha referencia egyel erősebb kötelék, azaz a GC megpróbálja minél tovább benntartani a hivatkozott objektumot a memóriában. Persze garancia ugyanúgy nincs rá, hogy még megtaláljuk, amikor kell nekünk, de gyorstárak megvalósításához ez a referencia ajánlott. Abban viszont biztosak lehetünk, hogy a program memóriaigényét az így megvalósított cache nem korlátozza, mivel a GC kitakarít minden olyan objektumot amire csak puha referencia hivatkozik mielőtt OutOfMemoryException-t dobna.
Referencia sorok
Ha egy WeakReference objektum egy már megsemmisített objektumra hivatkozik akkor már igazából ő maga is fölöslegessé válik. Azaz a programunknak időről időre érdemes kitakarítania a nem funkcionáló gyenge referenciákat. Ehhez nyújt segítséget a ReferenceQueue. Ha egy gyenge referenciának a konstruktorában megadunk egy ReferenceQueue-t akkor miután az általa hivatkozott objektum elérhetetlenné válik, belekerül a queue-ba. Igazából a WeakHashMap is azon az elven működik, hogy a kulcs elérése előtt leellenőrzi a saját ReferenceQueue-jában hogy nincs-e érvénytelen gyenge referencia, ha van akkor eltávolítja azt.
Fantom referencia
Végül a fantom referencia segítségével a java finalize-t tudjuk „kiterjeszteni”. Segítségével pl. külső erőforrás felszabadításokat végezhetünk . A fantom referencia konstruktorában mindig meg kell adni egy referencia sort. A referencia általa hivatkozott objektum finalize metódusa után, de a memóriából tényleges kikerülése előtt kerül be ebbe a megadott sorba. Ekkor az objektum már törlésre van ítélve és azt nem is lehet visszaállítani, ezt biztosítva a phantomReference get függvénye mindig null értékkel tér vissza. A tényleges törlés azonban csak akkor történik meg ha a phantom reference-nek a clear metódusát meghívjuk. Összefoglalva, ha egy fantom referencia által hivatkozott objektumot a GC fel akar szabadítani akkor meghívja az objektumra a finalize metódust, majd behelyezi a fantom referenciát a megadott referencia sorba, de magát az objektumot csak akkor tudja felszabadítani ha a fantom referenciának meghívják a clear metódusát..
WeakHashMap
A WeakHashMap egy gyenge referenciákat tartalmazó hasznos kis HashMap, azaz a kulcs-érték párból a kulcs egy gyenge referencia, így nem tartja a memóriában a hivatkozott objektumokat. Továbbá kényelmes a használata, mert adatszerkezet automatikusan kitakarítja magából a nem létező hivatkozásokat, azaz ha a kulcs által hivatkozott objektum megszűnik akkor a WeakHashMap-ből a kulcs-érték pár is kitörlődik. Használatánál oda kell figyelni arra, hogy bármikor eltűnhetnek belőle kulcs-érték párok, hisz a GC bármikor takaríthat, illetve a kulcs alapján való érték visszakeresés se triviális minden esetben, hisz ha van egy erős referenciánk a kulcsra, akkor nincs sok értelme az egésznek.
Objektum elérhetősége
A különböző objektumok elérhetőségét a rá hivatkozott referenciák erőssége szerint csoportba tudjuk sorolni. A következő lista felsorolja a különböző elérhetőségi szinteket legerősebbtől kezdve a leggyengébbig:
- erősen elérhető egy objektum ha hivatkozik rá egy referencia.
- Puhán (softly) elérhető egy objektum ha nem erősen elérhető de hivatkozik rá egy puha referencia
- Gyengén elérhető egy objektum ha se nem erősen se nem puhán nem elérhető de hivatkozik rá egy gyenge referencia
- Fantom elérhető egy objektum ha se nem erősen se nem puhán sem gyengén nem elérhető de hivatkozik rá egy fantom referencia.
- Nem elérhető egy objektum ha a fenti listában egyik kapcsolódási típus sem igaz rá.
Az idevonatkozó Java API: http://java.sun.com/javase/6/docs/api/java/lang/ref/package-summary.html