Wie funktioniert WeakReference?

A

anfänger12345

Kann mir jemand - so einfach wie nur irgendwie möglich - erklären wie genau WeakReference funktioniert und welche Anwendungsmöglichkeiten ich damit habe ?
 
Hallo,

WeakReferences verwendet man vor allen Dingen in Caches. Mit einem WeakReference kann man auf eine Instanz verweisen ohne den gc daran zu hindern die Zielinstanz wegzuräumen. Ein normaler Verweis auf eine Instanz A von einer anderen Instanz B aus hält die Instanz A fest solange B von einem Thread aus erreicht werden kann (das nennt man auch Starke Referenzierung, Strong Reference). Das führt dazu das Objekte länger als nötig im Speicher gehalten werden. Um dem Abhilfe zu schaffen hat man das java.lang.ref API entwickelt um die Art der Referenzierung und somit indirekt das Freigabeverhalten des GC bezüglich bestimmter Instanzen beeinflussen zu können.

Die von dir genannten WeakReference wrappen eine Referenz auf eine Instanz und referenzieren diese für die VM nur Schwach (weak reference). Im GC sind nun spezielle Behandlungsroutinen für die zahlreichen Reference Varianten (WeakReference,SoftReference,PhantomReference) hinterlegt. Sprich wenn eine Instanz nur noch von einer oder auch mehreren WeakReferences referenziert wird kann die Instanz vom gc weggeräumt werden.
Wie eingangs erwähnt ist dieses Verhalten gerade für speichereffiziente Caches sehr hilfreich. Zum Beispiel kann man den Eintrag aus der internen Datenstruktur entfernen, wenn die Schlüssel-Instanz gc'ed wurde) Ob eine bestimmte Instanz vom GC weggeräumt wurde kann man dann über eine ReferenceQueue mitbekommen. Wird eine Instanz einer Reference welche mit einer ReferenceQueue assoziiert war weggeräumt, so kann diese Reference über referenceQueue.poll() / remove() abgefragt werden -> man bekommt hier quasi einen Benachrichtigungsmechanismus für die Garbage Collection. Das ist ein verlässlicherer Mechanismus als finalize() (böse) zu überschreiben.

Mehr dazu findest du hier:
http://java.sun.com/javase/6/docs/api/java/lang/ref/package-summary.html

Das Konzept der WeakReferences gibt es natürlich auch in anderen Sprachen mit GC:
http://msdn.microsoft.com/de-de/library/system.weakreference.aspx

Mit der java.util.WeakHashMap<K, V> gibt es auch im JDK eine Map Implementierung welche WeakReferences verwendet und
sich als Cache eignet.

Eine sehr elegante Implementierung einer hoch konfigurierbaren map mit einstellbaren Reference Modi findet man in den guava Libraries (oder auch in googlecollections) mit MapMaker:
http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/collect/MapMaker.html

Hier mal ein kleines Beispiel zur Funktionsweise von WeakReferences:
Java:
package de.tutorials;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class WeakReferenceExample {
  public static void main(String[] args) throws Exception {

    final ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    executorService.execute(new Runnable() {
      public void run() {
        while (true) {
          try {
            Reference<? extends Object> ref = refQueue.remove();
            System.out.println("Reference reclaimed by gc: " + ref);
            
            break;
            
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    });

    Object o = new Object();
    System.out
      .println("0 instance o is created and associated with a WeakReference which is bound to a ReferenceQueue ");
    WeakReference<Object> weakRef = new WeakReference<Object>(o, refQueue);

    System.out.println("1 Until o is gc'ed the weakRef.get() will return the associated instance");
    System.out.println("1 " + weakRef.get());
    System.out.println("1 We set o = null to make the instance eligible for gc");
    o = null;

    for (int i = 0; i < 5; i++) {
      Object oo = weakRef.get();
      if (oo != null) {
        System.out.println("2 Seems that the gc didn't run yet...");
      }
      TimeUnit.SECONDS.sleep(1);
    }

    System.out.println("3 Seems that the gc didnt run yet... let's try to trigger gc manually...");
    
    while(weakRef.get() != null){
      System.gc();  
    }
    
    System.out.println("4 now o is really gone...");

    
    executorService.shutdownNow();
  }

}

Ausgabe:
Java:
0 instance o is created and associated with a WeakReference which is bound to a ReferenceQueue 
1 Until o is gc'ed the weakRef.get() will return the associated instance
1 java.lang.Object@3d4b7453
1 We set o = null to make the instance eligible for gc
2 Seems that the gc did't run yet...
2 Seems that the gc did't run yet...
2 Seems that the gc did't run yet...
2 Seems that the gc did't run yet...
2 Seems that the gc did't run yet...
3 Seems that the gc did't run yet... let's try to trigger gc manually...
4 now o is really gone...
Reference reclaimed by gc: java.lang.ref.WeakReference@5b86d4c1

Gruß Tom
 
Vielen Dank für die schnelle und umfangreiche Antwort... Auch die Links waren sehr hilfereich..

Aktuell schreibe ich ein Programm zur Patientenverwaltung und überlege ob es Sinn macht hier mit WeakReference zu arbeiten, um zum Beispiel Patienten wieder zu löschen.

Mfg
 
Zurück