Advanced   Java   Services Weak References Back Next Up Home


Starke Referenzen

Referenzen der folgenden Art

StringBuilder sb = new StringBuilder();
StringBuilder sb2 = sb;  // zweite Referenz auf das StringBuilderObjekt
sb = null; // nur noch eine Referenz auf das Objekt;
//...
sb2 = null; // keine Referenz mehr auf das Objekt

wollen wir in diesem Kapitel starke Referenzen nennen. Sie referenzieren ein Objekt im Hauptspeicher. In C++ würde man einfach sagen 'sb ist ein Pointer'. Findet der GarbageCollector keine Referenz mehr auf dieses Objekt so löscht er es. In der obigen Situation ist das (irgendwann) nach dem letzten Statement der Fall.


Schwache Referenzen

Neben der starken Referenz gibt es in Java drei abgeschwächte Referenzen. Sie leiten sich alle von der abstrakten Klasse java.lang.ref.Reference<T> ab und befinden sich im selben Package.

Sie sind oben von schwach nach noch schwächer geordnet. Da Phantomreferenzen selten gebraucht werden, wird hier das Verhalten von soften und schwachen Referenzen gezeigt. Zunächst wir kein Objekt gelöscht, auf das es noch eine starke Referenz gibt. Gibt es nach dem Verschwinden der starken Referenzen noch schwache Referenzen, so entscheidet der GC wann auch diese gelöscht werden.


SoftReference

Eine SoftReference wird vom GC dann gelöscht, wenn der Speicher knapp wird. So verbessern etwa speicheraufwendige Graphikobjekte die Performance eines Programms, aber wenn der Speicher knapp wird kann man nicht verwendete Objekte löschen und bei Bedarf nachladen. Mit SoftReferences kann man genau dieses Verhalten codieren. Das folgende kleine Beispiel legt eine starke und eine softe Referenz an. Nach dem Löschen der starken Referenz bleibt die SoftReference zunächst bestehen. Erst wenn der Speicher knapp wird reagiert der GC.

private static void softReferenceDemo1()
{
   Object ob = new Object(); // starke Referenz
   SoftReference<Object> sr = new SoftReference<>(ob); // Softreferenz auf ob
   System.out.println("Starke Referenz  : " + ob);
   System.out.println("Softe Referenz: " + sr.get());
   System.out.println("Starke Referenz löschen");
   ob = null;
   System.out.println("Softe Referenz: " + sr.get());
   System.out.println("GarbageCollector anstoßen");
   System.gc();
   try { Thread.sleep(2000); } catch(InterruptedException ex) {}
   System.out.println("softe Referenz nach 2 Sekunden: " + sr.get());
   // wird erst gelöscht, wenn speicher anderweitig gebraucht wird
   System.out.println("Großes int-array anschaffen");
   int[] arr = new int[44687000]; // Größe rechnerabhängig!
   //System.gc();
   try { Thread.sleep(1000); } catch(InterruptedException ex) {}
   System.out.println("Softe Referenz danach: " + sr.get());
}

Hier die Ausgabe des Programms:

Starke Referenz  : java.lang.Object@1db9742
Softe Referenz: java.lang.Object@1db9742
Starke Referenz löschen
Softe Referenz: java.lang.Object@1db9742
GarbageCollector anstoßen
Softe Referenz nach 2 Sekunden: java.lang.Object@1db9742
Großes int-array anschaffen
Softe Referenz danach: null

WeakReference

Eine schwache Referenz wird dagegen vom GC gelöscht, wenn es keine starke Referenz mehr auf das Objekt gibt. Meist hilft hier ein Anstoßen des GC mit der Methode System.gc(). Wie alle schwachen Referenzen ist auch die WeakReference typisiert. das folgende Beispiel zeigt das verhalten.

/**
*  Erzeugen einer WeakReference
*  Verhalten einer WeakReference
*  nach dem Löschen der starken Referenz wird die schwache Referenz durch den GC gelöscht
*/
private static void weakReferenceDemo1()
{
   Object ob = new Object(); // starke Referenz
   WeakReference<Object> wr = new WeakReference<>(ob); // schwache Referenz auf ob
   System.out.println("starke Referenz  : " + ob);
   System.out.println("schwache Referenz: " + wr.get());
   System.out.println("starke Referenz löschen");
   ob = null;
   System.out.println("schwache Referenz: " + wr.get());
   System.out.println("GarbageCollector anstoßen");
   System.gc();
   System.out.println("schwache Referenz: " + wr.get());
}

das Programm macht die folgende Ausgabe:

Starke Referenz  : java.lang.Object@1db9742
Softe Referenz: java.lang.Object@1db9742
Starke Referenz löschen
Softe Referenz: java.lang.Object@1db9742
GarbageCollector anstoßen
Softe Referenz nach 2 Sekunden: java.lang.Object@1db9742
Großes int-array anschaffen
Softe Referenz danach: null


ReferenceQueue

Mit einer ReferenceQueue kann der Entwickler erkennen, wann der GC eine schwache Referenz gelöscht hat. Übergibt man dem Konstruktor beim Anlegen einer schwachen Referenz eine (starke) Referenz auf eine ReferenceQueue, so legt der GC nach dem Löschen des Objekts die schwache Referenz in der ReferenceQueue ab. Landet die schwache Referenz in der Queue, so weiß man, daß das Objekt gelöscht wurde. das Objekt ist dann nicht mehr verfügbar, ein Aufruf von get() liefert null. das folgende Beispiel zeigt dieses Verhalten für eine schwache Referenz.

/**
* WeakReference und ReferenceQueue
* Eine schwache Referenz wird in die ReferenceQueue aufgenommen nachdem die
* starke Referenz gelöscht wurde. Sie ist dann auch über eine schwache
* Referenz nicht mehr erreichbar.
*/
private static void weakReferenceQueueDemo()
{
   Object ob = new Object();  // starke Referenz
   ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
   WeakReference<Object> wr = new WeakReference<>(ob, refQueue) ; // schwache Referenz
   System.out.println("starke Referenz  : " + ob);
   System.out.println("schwache Referenz: " + wr.get());
   System.out.println("poll referenceQueue: " + refQueue.poll());  //null
   System.out.println("=== starke Referenz löschen ===");
   ob = null;
   System.out.println("schwache Referenz: " + wr.get());
   System.out.println("GarbageCollector anstoßen");
   System.gc();
   try { Thread.sleep(1000); } catch(InterruptedException ex) {}
   System.out.println("schwache Referenz: " + wr.get());
   Reference<? extends Object> polledReference = refQueue.poll();
   System.out.println("poll referenceQueue : " + polledReference);  // java.lang.ref.WeakReference@c3c749
   System.out.println("polled reference get: " + polledReference.get());  // java.lang.ref.WeakReference@c3c749
   System.out.println("poll referenceQueue again: " + refQueue.poll());  //null
}

Die Ausgabe

starke Referenz  : java.lang.Object@1db9742
schwache Referenz: java.lang.Object@1db9742
poll referenceQueue: null
=== starke Referenz löschen ===
schwache Referenz: java.lang.Object@1db9742
GarbageCollector anstoßen
schwache Referenz: null
poll referenceQueue : java.lang.ref.WeakReference@106d69c
polled reference get: null
poll referenceQueue again: null

WeakHashMap

Beim Arbeiten mit einer HashMap kann es vorkommen, daß ein Key gelöscht wird, bevor der entsprechende Eintrag in der Hashmap gelöscht wird. Das (Key, Value) paar ist dann nicht mehr zu entfernen, da der Key verloren ist. Hier kann man schwache Referenzen einsetzen. Im folgenden Beispiel verwenden wir eine HashMap, deren Keys schwache Referenzen sind. Wird ein Key gelöscht, so liefert ein get() auf eine schwache Referenz null. Da man die schwache Referenz aber noch erreichen kann, kann man die Einträge in der HashMap löschen programmatisch löschen. Das folgende Beispiel demonstriert das.

/**
 * A HashMap with WeakReferences as keys
 * die muß man selbst durchlaufen und löschen, falls die schwachen Referenzen auf null zeigen
 */
private static void weakHashMapDemo1() throws InterruptedException
{
   HashMap<WeakReference<Object>, String> whm = new HashMap<>();
   Object key1 = new Object();
   Object key2 = new Object();
   Object key3 = new Object();

   whm.put(new WeakReference<>(key1), "Storm");
   whm.put(new WeakReference<>(key2), "Wedekind");
   whm.put(new WeakReference<>(key3), "Mann");
   System.out.println("size = " + whm.size());
   key2 = null;
   System.out.println("size = " + whm.size());
   System.gc();
   TimeUnit.SECONDS.sleep(1);
   System.out.println("size = " + whm.size());
   Set<WeakReference<Object>> keys = whm.keySet();
   System.out.println("keyset:");
   for( WeakReference<Object> key: keys)
   {
      System.out.println(key.get());
   }
   WeakReference<Object>[] keyArr = keys.toArray(new WeakReference[0]);
   for(WeakReference<Object> key : keyArr)
   {
      if (key.get() == null)
      {
         whm.remove(key);
      }
   }
   System.out.println("size = " + whm.size());
}

Die Ausgabe des Programms:



size = 3
size = 3
size = 3
keyset:
java.lang.Object@25154f
java.lang.Object@10dea4e
null
size = 2

Angenehmerweise gibt es eine spezielle HashMap, die das Löschen der Keys automatisch erledigt: Weakhashmap. Damit vereinfacht sich das vorige Beispiel. hier das vereinfachte Programm.

/**
 */
private static void weakHashMapDemo2() throws InterruptedException
{
   WeakHashMap<Object, String> whm = new WeakHashMap<>();
   Object key1 = new Object();
   Object key2 = new Object();
   Object key3 = new Object();

   whm.put(key1, "Storm");
   whm.put(key2, "Wedekind");
   whm.put(key3, "Mann");
   System.out.println("size = " + whm.size());
   key2 = null;
   System.out.println("size = " + whm.size());
   System.gc();
   TimeUnit.SECONDS.sleep(2);
   System.out.println("size = " + whm.size());
}

Die Ausgabe

size = 3
size = 3
size = 3
keyset:
java.lang.Object@25154f
java.lang.Object@10dea4e
null
size = 2

Valid XHTML 1.0 Strict top Back Next Up Home