2012. december 30., vasárnap

Reflection - a bajkeverő csodagyerek

Albert Hoffmann szavai csengnek a fülemben, amikor a Reflection-re gondolok: "bajkeverő csodagyerek". Egyfelől milyen hasznos, hogy futásidőben példányosítani lehet osztályokat, hogy feltérképezhetjük őket, hogy módisíthatjuk őket, stb. Aki dolgozott már modern webes keretrendszerrel az tisztában van a Reflection minden előnyével, hisz ez a legegyszerűbb módja feltölteni egy objektumot a kéréssel érkező paramétereknek, vagy egy proxy segítségével megváltoztatni egy adattag viselkedését. Ilyen értelemben elképzelhetetlen lenne a (mai értelemben vett) Java EE terjedése, mert Reflection nélkül még mindig a kőkörban élnénk.
RequestParams rp = new RequestParams(); 
final Field[] fields = rp.getClass().getDeclaredFields();
for (final Field field : fields) {
 firstChar = String.valueOf(field.getName().charAt(0));
 methodName = "set" + field.getName().replaceFirst(firstChar, firstChar.toUpperCase());
 try {
  method = rp.getClass().getMethod(methodName, String.class);
  method.invoke(rp, request.getParameter(field.getName()));
 } catch (final SecurityException e) {
 } catch (final NoSuchMethodException e) {
 } catch (final IllegalArgumentException e) {
 } catch (final IllegalAccessException e) {
 } catch (final InvocationTargetException e) {
 }
}
A példa jól szemlélteti, hogy pofon egyszerű feltölteni a kapott paraméterekkel egy objektumot, újabb paraméterek létrehozásakor nem kell setterekkel bajlódni, ráadásul a kód teljesen hordozható, így bármely osztályra dinamikusan alkalmazható (a példa csak String-eket kezel az egyszerűség kedvéért).
Itt merül fel a kérdés, hogy a Reflection nem egy kétélű fegyver? Mivel mondhatni teljes hozzáférést biztosít objektumjaink belső működéséhez és felépítéséhez, nem lehet ezt rosszra is használni? A válasz egyértelmű IGEN! Daily WTF-en találtam ezt a kedves kis szösszenetet:
class ValueMunger extends Thread {
    public void run() {
        while(true) {
            munge();
            try { sleep(1000); } catch (Throwable t) { }
        }
    }
    
    public void munge() {
        try {
            Field field = Integer.class.getDeclaredField( "value" );
            field.setAccessible( true );
            for(int i = -127; i <= 128; i++)
            field.setInt( 
                Integer.valueOf(i),
                // either the same (90%), +1 (10%), or 42 (1%)
                Math.random() < 0.9 ? i : Math.random() < 0.1 ? 42 : i+1  );
        } catch (Throwable t) { ; }
    }

}
A fenti kódrészlet az Integer wrapper cachet-t zagyválja egy kicsit össze, időnként nem a valós értéket kapjuk vissza, hanem az élet értelmét, ami üzleti kritikus alkalmazásokban igen nagy probléma, nem szeretnénk egy pészmékerben ilyesmi kóddal találkozni, de azt sem szeretnénk, ha a banki alkalmazás viccelné meg a bankszámlánkat időnként. A final mezőink, a private tagjaink sincsenek biztonságban, ahogy az Enum-jaink sem, és ezen a ponton a végtelenségi lehetne sorolni az ártalmas és vicces példákat, ugyanis kijelenthető, hogy (Sun/Oracle) Java verziótól függően szinte mindenhez hozzá lehet férni Reflection API segítségével, nincs menekvés.
Ártalmas kódot hagyhat hátra kilépő munkatárs, tölthetünk le ártatlanul egy külső osztálykönyvtárral, szóval több forrásból is beszerezhetőek, de vajon mit lehet tenni ellenük? Az egyetlen gyógyszer a tudatos és körültekintő felhasználás.

  • Használjunk statikus kódelemzőt, és szigorúan tartsuk számon azokat a részeket, ahol az alkalmazásunk Reflection API-t használ.
  • Használjunk nyílt forrású könyvtárakat, és fordítsuk magunk (kódelemzés után).
  • A letöltött osztálykönyvtárak hitelességét minden lehetséges módon ellenőrizzük, ugyanis koránt sem biztos, hogy senki nem törte fel kedvenc keretrendszerünk weboldalát, és cserélte ott le a letölthető jar-okat saját módosított verziójára (jó lenne a Linux repository-khoz hasonló központosított megoldás).
  • Csak a készítő által aláírt osztályokat, és jar-okat használjunk, ha külső forrásra kell támaszkodnunk.
  • Minimalizáljuk a külső forrásokat, ne használjunk két külön osztálykönyvtárat közel ugyanarra a feladatra.
Ha van még ötletetek, milyen módszerekkel védhetjük még a hátsónkat, kérlek ne tartsátok vissza magatokat, és írjátok meg, nekem hirtelen ennyi jutott eszembe. A bejegyzésben taglalt téma nem újkeletű, de azt hiszem sosem lehet róla eleget beszélni.

2012. november 4., vasárnap

Eclipselink beizzítása Jboss 7.1 környezetben

Valaha fejlesztettünk egy portált Liferay platformon. Mivel a fejlesztés elején a gyári Service Buildert elvetettük, teljesen kézenfekvő megoldás volt EJB/JPA párossal implementálni az üzleti és a perzisztens réteget. Első körben Hibernatere esett a választás, mert a Jboss alkalmazásszerver "natívan" támogatta, nem volt más dolgunk, mint deployolni az alkalmazást, és működött. Fejlesztés során nem is akadt semmi problémánk, ám az élesítés előtti utolsó hajrában szembesültünk azzal a ténnyel, hogy a perzisztens réteg teljesítménye a béka feneke alatt van, majd egy kis kutakodás után kikristályosodott, hogy a Hibernate nem bánik túl kedvezően az erőforrásokkal. Nincs mese, másodlagos cachet kell beüzemelni (Ehcachere esett a választás), de sajnos még ezzel sem hozta azt az alkalmazás, amit elvárnánk/megszoktunk. Ekkor jött az ötlet, hogy dobjuk ki a Hibernatet. Eclipselinkkel már volt tapasztalatunk, ráadásul nem is rossz, így megejtettük a váltást. Egyáltalán nem bántuk meg, a teljesítmény az egekbe szökött (pontos számadataink nincsenek, egyszerűen nem bírtuk elérni, hogy az adatbázisszerver CPU 10% fölé menjen, az addigi konstans 80% helyett). Eclipselinkre való átállás nem volt zökkenőmentes, nem pont ugyanúgy értelmezi a JPQL-t mint a Hibernate (egész pontosan az Eclipselink JPQL-t valósít meg, míg a Hibernate a saját HQL-jét használja), ezen felül a Jbossból is ki kellett gyomlálni pár Hibernate jart, úgy, hogy közben a Liferay, ami egyébként Hibernatre épül, még működőképes maradjon.

Az élesítés óta megjelent a Liferayből 2 újabb verzió, és szerettem volna frissen tartani a rendszert, de természetesen az új Liferayhez új Jboss is dukál. A frissítendő architektúra hibridsége miatt nem volt könnyű szülés a művelet, ennek lépéseit szeretném megosztani.

Az új Jboss architektúra teljesen modularizált, ezért egy 3rd party lib használata nem annyi, hogy bemásoljuk a megfelelő jar-t a classpathra, hanem modulként definiálnunk kell a rendszerben. Nincs ez másként az Eclipselinkkel sem. Modul létrehozásához annyit kell tennünk, hogy létrehozzuk a modules/org/eclipse/persistence/main könyvtárakat az alkalmazásszerverünkben, és a main könyvtárba bemásoljuk az eclipselink.jart, a konfigurációhoz pedig létre kell hoznunk ugyanitt egy module.xml-t, amiben a modult magát definiáljuk.


 
 
 
  
  
  
  
  
  
  
  
  
  
  
  
 

Az előző lépéshez hasonlóan egy Ant, és egy adatbázis modult is létre kell hozni (estemben egy PostgreSQLt).

    
        
    


    
        
    
    
        
        
        
    

Ezután nincs más dolgunk az alkalmazásszerveren, mint a standalone/configuration/standalone.xml-t szerkeszteni. Először is definiáljuk a szükséges DataSource-ot.
        
                
                    jdbc:postgresql://[hostname]:[port]/[database]
                    postgres
                    
                        14
                        20
                    
                    
                        postgres
                    
                
                
                    
                        org.postgresql.xa.PGXADataSource
                    
                
            
Lehet, hogy ez Liferay specifikus, de nálam volt Hibernate konfiguráció is ebben az xmlben, ami miatt timeoutolt a perzisztens réteg inicializálása, így azt egy kecses mozdulattal kitöröltem (egyelőre nem jelentkezett a hiányából fakadó probléma).

Miután az alkalmzásszerver konfigurációjával végeztünk, már csak az alkalmazásunkat kell egy kicsit reszelgetni. Ahol JNDI név szerint hivatkoztunk EJB Session Beanekre, ott a hivatkozott nevet célszerű átírni. A Jboss az EJB deployment során kiírja, hogy milyen neveken regisztrálta a beaneket, nekem nem mindegyik működött ??, de ez a forma bevált: global/[appname]/[serviceimplclassname].
A persistence.xml-ben is be kell állítanunk pár beállítást.
org.eclipse.persistence.jpa.PersistenceProvider
java:/jboss/datasources/LiferayPool

 
 


Utolsó lépésként az alkalmazásunkban be kell állítani, hogy milyen Jboss modulok a függőségei, ezt két féle módon is megtehetjük, vagy a META-INF/MANIFEST.MF állományban vesszük fel az alábbi sort:
Dependencies: org.eclipse.persistence
vagy létrehozunk ugyanitt egy jboss-deployment-structure.xml nevű konfigurációs állományt, az alábbi tartalommal:

  
    
      
    
  

Mindenkinek javaslom, hogy tegyen egy próbát az Eclipselinkkel, számos területen jobb, mint a Hibernate, nem csak teljesítményben és szabványkövetésben. Kedvenc tulajdonságom pl, hogy Hibernattel ellentétben a Session bezárása után is eléri még a DataSourcot (limitáltan), így a lustán inicializált relációkat nem kell kézzel betöltögetni még az üzleti rétegben, hanem a helyükre tett proxyn keresztül később is eléri azokat.

2012. június 27., szerda

Barátkozás a Groovyval

Régóta terveztem, hogy megismerkedek a Groovy rejtelmeivel, és most úgy alakult, hogy egy hosszan tartó barátság első napjait élem. A nyelv ismertetést nem is az alapoknál kezdeném, hiszen azok egy Java fejlesztőnek nem szabad, hogy gondot okozzanak, mivel visszafelé teljes a kompatibilitás. Sokkal inkább koncentrálnék a kedvenc programozói eszközökre, amikkel új dimenziókba lehet helyezni az eddig megszokott Java programozást.

Van két fontos különbség a Java és a Groovy között, amit mindenképpen megosztanék ezen a ponton. Groovyban nincsenek primitív típusok, még ha látszólag úgy is deklarálunk egy változót, az eredmény mindig egy példány lesz a memóriában. A másik, hogy a dupla egyenlőség vizsgálat, Javatól eltérően érték szerint hasonlít össze!

Minden Java fejlesztő rémálma a NullPointerException, éppen ezért Javaban a műveletek nagy részét megelőzi erre vonatkozó ellenőrzés, ami csak átláthatatlanabbá teszik a kódot. Groovyban ez a teljes vizsgálat elvégezhető egyetlen kérdőjel segítségével:

int foo = bar?.length ? bar.length : -1;
Az eredmény, sokkal tömörebb kód, miközben az olvashatóságot sem rontja a szintaxis. A történet egyszerű. A kérdőjel helyére egy null ellenőrzést ékel a fordító.

Következő hasznos egyszerűsítés amit meg szeretnék említeni az un. Elvis operátort.
int foo = bar ?: -1;
Az Elvis operátorral az alapértelmezett értéket lehet meghatározni, amennyiben az eredeti "érték" false vagy null.

Soron következő kedvencem a GString. Stringek összefűzésének problémájával a legtöbben már egészen biztos találkoztunk. Kis mennyiségű szöveg összefűzésénél még nem is akkora a probléma, mert az egy sorban elvégzett String összefűzés automatikusan egy StringBulder osztályra fordul. Nagyobb mennyiség esetén (hallottam olyan helyről, ahol a mai napig 80 karakter sorhossz) macerássá válik a művelet. A Groovy eszköztárában egy az Expression Language-re kísértetiesen hasonlító megoldást építettek.
String foo = "Foo"
String bar = "${foo} Bar" 
Ezt a funkcionalitást kombinálva a több soros stringek deklarációjával, máris kézzelfogható előnyhöz jutunk:
def sql = """
select * from ${table}
where bar = ${foo}
"""
Fontos tudni, hogy a szimpla idézőjelek között létrehozott 'stringek' hagyományos java.lang.String példányok lesznek, a duplával pedig GStringek, ezért ha nem szeretnénk a GString sajátosságait kihasználni, mindig szimpla idézőjellel példányosítsuk stringjeinket.

Reguláris kifejezések használatát is lényegesen leegyszerűsítették a Groovys srácok.
Pattern pattern = ~/(.*)/
boolean find = 'foo' ==~ pattern
Matcher m = 'foo' =~ pattern

Mint ahogy a bevezetőben említettem, Groovyban nincsenek primitív típusok, most lássuk, hogy ennek miért is van jelentősége. A fordító bizonyos operátorokat automatikusan átfordítja az objektum metódus hívásaira.
a + b // a.plus(b)
a − b // a.minus(b)
a ∗ b // a.multiply(b)
a ∗∗ b // a.power(b)
a / b // a.div(b)
a % b // a.mod(b)
a | b // a.or(b)
a & b // a.and(b)
a ^ b // a.xor(b)
a++ o r ++a // a.next()
a−− o r −−a // a.previous()
a [ b ] // a.getAt(b)
a [ b ] = c // a.putAt(b, c)
a << b // a.leftShift(b)
a >> b // a.rightShift(b)
~a // a.bitwiseNegate()
−a // a.negative()
+a // a.positive()
a <=> b : a.compareTo(b)
Ennek előnye egyrészt, hogy megkíméli a programozót rengeted felesleges gépeléstől, másrészt ezt a működést kihasználva saját osztályainkat is fel tudjuk készíteni, hogy értsék a különböző operátorokat. A Groovyban van is erre jó példa, pl. a Date osztályban.
def today = new Date()
def tomorrow = today + 1
def yesterday = today - 1
assert today.plus(1) == tomorrow
assert tomorrow.minus(1) == today
Fontos megértenünk 2 dolgot az operátorok átfordítása kapcsán. Az egyik, hogy vannak esetek, amikor a visszatérési objektum tipusa más lesz, mint a operandusé.
StringBuilder sb = 'bar' << 'foo'
A másik dolog, a túlcsordulást elhárító típusbővítés, ami azt jelenti például, hogy az 1 + 1.5 az ((BigInteger) 1.5).plus(1) -ra fordul, és az eredmény egy BigDecimal osztályban kerül tárolásra, hiába az Integer állt előbb. A Groovy decimális számok tárolására alapértelmezetten a BigDecimalt használja, elkerülendő a lebegőpontos számok ábrázolásából fakadó hibákat.

A következő érdekesség amire szeretném felhívni a figyelmet a Groovy osztálykezelése. A Groovy egy speciális osztályon keresztűl hozzáférést biztosít az osztályokhoz, és lehetőséget ad azok bővítésére.
String.metaClass.prefixFirstLette = { prefix ->
    return "${prefix}_${delegate.substring(0, 1)}"
}
println 'bar'.prefixFirstLette('foo');

Az előző példában egy újabb speciális Groovy osztállyal találkozhattunk, a Closure-val, mely osztály kiemelten fontos a nyelv szempontjából, és számtalan metódusnak átadható paraméterként.
Closure c = { i ->
    return i
}
println c.call(1)
A Closure segítségével a Groovy szimulálni tudja a Javaból egyébként igencsak hiányzó névtelen függvények használatát.

Következő témakör, amelyet fontos kihangsúlyozni a Groovyval kapcsolatban, hogy natív támogatást nyújt listák és mapok kezelésére, ráadásul számos olyan funkcióval egészítették ki ezen osztályokat, amik megkönnyítik a velük végzett műveleteket. Pár példa a teljesség igénye nélkül:
def words = ['ant', 'buffalo', 'cat', 'dinosaur']
assert words.findAll{ w -> w.size() > 4 } == ['buffalo', 'dinosaur']
assert words.collect{ it[0] } == ['a', 'b', 'c', 'd']

def list = [[1,0], [0,1,2]].sort { item -> item.size() }
assert list == [ [1,0], [0,1,2] ]

assert [1, 3, 5] == ['a', 'few', 'words']*.size() //minden elemen végrehajtja a size() metódust
A GDK plusz extraként kiegészít minden tömböt, kollekciót, és Stringet egy további toList() metódussal.
def greeting = 'Hello Groovy!'
assert greeting[6..11] == 'Groovy'
assert greeting[0,2,4] == 'Hlo'

A következő érdekesség az XML kezelés Groovyban. Okulva a Java hiányosságából, szintén natív támogatás van XML struktúrák kezelésére.
def builder = new groovy.xml.MarkupBuilder()
builder.book {
    author 'Bar Foo'
    title 'sometitle'
    properties {
        pages 42
    }
}
println builder

  Bar Foo
  sometitle
  
    42
  

A Streamek kezelésében is hoz változást a Groovy. Javaval ellentétben nem kell ciklust írnunk a tartalom áttöltéséhez.
def address = 'http://jpattern.blogspot.com/favicon.ico'
def file = new FileOutputStream(address.tokenize("/")[-1])
def out = new BufferedOutputStream(file)
out << new URL(address).openStream()
out.close()

Utoljára hagytam a legkevésbé fontos, de talán mégis hasznos újítást az importok területén. Lehetőség van Groovyban importált osztály-t aliasszal megjelelölni.
import org.springframework.context.i18n.LocaleContextHolder as LCH
...
def locale = LCH.getLocale()

A pozitívumok után következzenek a negatívumok, bár személy szerint nem sok ilyet találtam. Az első, hogy a Groovy nem támogatja belső osztályok definiálását, ami szerintem a Java eszköztárának egy fontos kelléke. A Másik, hogy dinamikus típusú nyelv lévén az IDE támogatás meg sem közelíti a Javaét. Bár mindhárom elterjedt IDE (Netbeans, Eclipse, IntelliJ) rendelkezik Groovy támogatással, Javahoz szokott fejlesztőként számtalan kényelmi funkciót kell nélkülözni.
Véleményem szerint egy elég erőteljes nyelv lett a Groovy, a fejlesztők igyekeztek a Java hiányosságaiból tanulni, miközben megőrizték a Javaban rejlő erőt teljes mértékben. Bár mindenki azt csinál amit akar, én személy szerint alapos Java ismeretek nélkül nem ajánlom a nyelvet kezdőknek, ugyanis ahhoz elég sok dologban tér el a Javatól, hogy rossz szokásokat fejlesszen későbbi Java programozáshoz. Ilyen pl. a dupla egyenlőség vizsgálat, a String automatikus StringBuilderré alakítása bizonyos operátorok használatakor, A streamek kezelése, stb. Remélem további ismerkedésre inspirál mindenkit ez a kis írás, és sokan kiegészítik ezzel a remek eszközzel programozói repertoárjukat.

2012. március 23., péntek

Könyvajánló: Java fejtörők

A Szabad Szoftver Konferencián volt alkalmam megvásárolni Joshua Bloch és Neal Gafter, Java fejtörők című könyvét. Bevallom őszintén elsősorban az akciós ára miatt vásároltam meg, ám azonban azóta az egyik kedvenc olvasmányom lett, mert mindamellett, hogy sokat lehet belőle tanulni, és megmozgatja a fogaskerekeket, még szórakoztat is.
Lássuk először a szerzőket. Joshua Block neve ismerősen csenghet, a Carnegie Mellon University-n doktorált, a Java 5 nyelvi bővítésein dolgozott és a Java Collections Framework tervezését és fejlesztését is irányította többek közt. Dolgozott a Sun Microsystemsnél és jelenleg a Google egyik főmérnöke. Szerzője a díjat nyert Hatékony Java című könyvnek. Ez utóbbi műve szerintem kötelező olvasmány minden Java fejlesztőnek! Neal Gafter a University of Rochesteren szerzett doktorátust, majd az Sun Microsystemsnél irányította a Java fordító fejlesztését rangidős mérnökként. Jelenleg ő is a Googlenél - kinél másnál - dolgozik szoftvermérnökként.
A könyv a Java nyelvben rejlő csapdákkal, buktatókkal, és olyan szélsőséges esetekkel foglalkozik, amelyekbe nap mint nap belefuthatunk munkánk/hobbink során, és kellő ismeret nélkül csak a fejünket vakargathatjuk a nem várt eredmény miatt. A könyv 9 gyakorlati fejezetbe kategorizálja az összesen 95 esetet, és az egyszerűbbtől a bonyolultabb példák felé halad, a leg harcedzettebbeknek is sokszor feladva a leckét. Nem spoilerezek többet, tessék megvenni a könyvet, ha még nincs meg :).

2012. január 21., szombat

Weak és Soft referenciák a Javaban

Több éves Java tapasztalattal a hátam mögött hallottam először a gyenge (weak) referenciák létezéséről a nyelvben, és úgy gondoltam érdemel pár szót a téma kör. Javaslom, akinek nem világos a JVM szemétgyűjtőjének (továbbiakban GC) működése, ezen a ponton olvassa el Viczián István idevágó bejegyzését. Tehát mint tudjuk a GC kidob a memóriából minden olyan objektumot, melyre nem mutat már egyetlen referencia sem. Amikor létrehozunk egy objektumot, Integer foo = new Integer(0), akkor a foo egy strong reference lesz rá, és amíg az objektum "strongly reachable", addig a GC-nek tabu. Mivel ezeket az objektumokat nem tudja a GC felszabadítani, a memória beteltével jön a fránya OutOfMemotyError, és az alkalmazás kilép. Egy darabig persze lehet növelni a memóriát, majd a fizikai határok elérésével lehet elosztani az alkalmazást, de előbb álljunk meg egy szóra! Vajon minden objektumra szükségünk van a memóriában? Kézenfekvő megoldás, hogy bizonyos objektumokat azonnal megszüntessünk, amint nincs rájuk szükség, viszont az objektumok újbóli létrehozása is nagy költség, ebben az esetben pedig értékes processzoridőt fecsérelünk az állandó memóriaallokációra, példányosításra, stb. Arany középútként az 1.2-es, igen az 1.2-es Java verzióban bevezették a weak referenciákat, melynek implementációja a WeakReference osztály. A WeakReference referenciát tárol az adott objektumra, annyi különbséggel, hogy ezt az objektumot a GC első futáskor, szó nélkül eltávolítja a memóriából, felszabadítva ezzel a helyet mások számára.

WeakReference<StringBuilder> weakString = new WeakReference<StringBuilder>(new StringBuilder());

int i = 0;
while (weakString != null && weakString.get() != null) {
 weakString.get().append(i++);
 System.out.println(i);
}

System.out.println("Finish");

Kezdetként futási paraméterként állítsuk be a JVM-nek a maximális memóriát mondjuk 2 Mb-ra (-Xmx2m), majd az értékkel játszva láthatjuk, hogy mennyit változik programunk kimenete. Ez a technika kiválóan alkalmas memória gyorsítótárak készítésére, ám amennyiben kulcs-érték-pár alapú gyorsítótárat készítünk a new WeakReference(myMapInstance) helyett használjuk, az erre a célra készített WeakHashMap implementációt. A WeakHashMap a kulcsokat őrzi weak referenciával, és a MapEntry automatikusan eltávolításra kerül, amikor a kulcs már nincs rendszeres használat alatt.

A WeakReference mellett létezik a SoftReference osztály is, amely azt garantálja, hogy az OutOfMemoryError előtt felszámolásra kerülnek az objektumok, helyet biztosítva az újonan létrejövőknek.

SoftReference softObj = new SoftReference(new Serializable() {
 @Override public void finalize() throws Throwable {
  System.out.print("Finalize runned");

  super.finalize();
 } 
});

StringBuilder sb = new StringBuilder("foobar");
while (true) {
  sb.append(sb.toString());
}
A kimenetben láthatjuk, hogy miközben az alkalmazás akkut memóriahiányban elhalálozott, még utolsó leheletével felszámolta osztályunkat.
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
Finalize runned at java.util.Arrays.copyOf(Arrays.java:2882)
 at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100)
 at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:390)
 at java.lang.StringBuilder.append(StringBuilder.java:119)
 at com.blogspot.jpattern.Main.run(Main.java:36)
 at com.blogspot.jpattern.Main.main(Main.java:22)
Java Result: 1
Természetesen ennél complexebb program esetében az alkalmazás tovább tud dolgozni a felszabadult memóriával.A Java specifikáció szerint semmi garancia nincs a finalize() metódus futására!

Evezzünk egy kicsit sötétebb vizekre. Ha jól megnézzük a Reference API dokumentációját, láthatjuk, hogy van még egy ismert implementáció, a PhantomReference. Az első különbség társaihoz képest, hogy a bele helyezett objektumra soha nem tudunk referenciát kérni, ugyanis konstans null értékkel válaszol a get() hívására. Másik nagy eltérés lényege tömören, hogy az ilyen objektumokat a GC csak azelőtt rendezi sorba, mielőtt a fizikai memóriából kitakarítaná. Mivel úgy tudom Darth Vader nagyurat is ennek használata állította át az erő sötét oldalára, én magam nem merészkedtem ennél tovább (esetleg írhatnátok valami konkrét használati esetet).

Előfordulhat, hogy az alkalmazásunkban szükséges tudni, hogy mely objektumokat dobta már ki a GC, és melyeket nem. Ilyen esetben egy ReferenceQueue osztályt kell példányosítanunk, és a queue példányt átadni a WeakReference vagy SoftReference konstruktorának. A bejegyzésben említett referenciákkal óvatosan bánjunk, és csak alapos tervezés és tesztelést követően alkalmazzuk éles bevetésben őket.