publish and discover academic work ...

Verwendung von SoftReference in Java

Damit Sie besser verstehen, worum es in diesem Artikel geht, empfehle ich Ihnen den Artikel zu lesen. Die Kenntnisse aus dem genannten Artikel sind obligatorisch, um den Inhalt dieses Artikels vollständig zu verstehen.

In diesem Artikel geht es um drei Arten von Referenzen:

  1. SoftReference
  2. WeakReference
  3. PhantomReference

Diese drei Klassen können uns helfen, die unangenehme Situation zu vermeiden, dass unsere Java-Anwendung mit OutOfMemoryError abstürzt. Ich möchte Ihnen anhand eines Beispiels zeigen, wie wir das Problem lösen können.

Zur Erinnerung, falls der JVM ein Absturz kurz bevor steht, etwa wegen Mangel an Speicher, wird sie versuchen nicht genutzte Objekte zu entfernen. Sie startet den Garbage Collector, der alle nicht referenzierten Objekte sammelt. SoftReference gilt in diesem Fall als keine echte Referenz. Objekte die nur mit einer SoftReference erreichbar sind, werden entfernt. Genau diese Eigenschaft können wir ausnutzen.

Angenommen wir haben eine Anwendung, die sehr intensive mit Bildern arbeitet. Dabei wird immer dasselbe Bild mit anderen Bildern verbunden (um beispielsweise eine Art Wasserzeichen zu erzeugen). Die einfachste Variante könnte ungefähr so aussehen:

public class ImageProcessor {
    private static final String IMAGE_NAME = "bigImage.jpg";
    public InputStream concatenateImegeWithDefaultVersion(InputStream userImageAsStream) {
        InputStream defaultImage = this.getClass().getResourceAsStream(IMAGE_NAME);                
        // calculate and return concatenated image
    }            
}


Der entscheidende Nachteil dieser Realisierung ist, dass wir jedes Mal das Bild neu laden müssen und das ist sehr ineffizient. Wir können das Bild cachen:

public class CachedImageProcessor {
    private static final String IMAGE_NAME = "bigImage.jpg";
    private InputStream defaultImage;           
    
    public InputStream concatenateImegeWithDefaultVersion(InputStream userImageAsStream) {
        if (defaultImage == null) {
            defaultImage = this.getClass().getResourceAsStream(IMAGE_NAME);
        }                
        // calculate and return concatenated image
    }            
}


Diese Vorgehensweise ist schon viel besser, ist aber auch nicht ideal. Wenn das Programm viel Speicher benötigt, kann es so passieren, dass es abstürzt, obwohl das Bild in diesem Moment überhaupt nicht benötigt wird. Letztendlich haben wir keine besonders gute Auswahl: entweder Effizienz oder Robustheit. Zum Glück können wir diese beide Vorteile vereinen, indem wir SoftReference verwenden:

public class SoftCachedImageProcessor {
    private static final String IMAGE_NAME = "bigImage.jpg";
    private SoftReference<InputStream> defaultImageRef = new SoftReference(loadImage());

    public InputStream concatenateImegeWithDefaultVersion(InputStream userImageAsStream) {                
        if (defaultImageRef.get() == null) {        //  1
            defaultImage = this.getClass().getResourceAsStream(IMAGE_NAME);
            defaultImageRef = new SoftReference(defaultImage);
        }        
        
        defaultImage = defaultImageRef.get();        //  2        
        // calculate and return concatenated image
    }            
}


Jetzt cachen wir das Bild. Wenn das Programm dringend mehr Speicher benötigt, wird das Bild entfernt und beim nächsten Zugriff aber wieder geladen. So einfach geht das. Allerdings gibt es an der Stelle eine Kleinigkeit, die unsere Anwendung zum Absturz bringen kann. Angenommen, dass in Zeile 1, die Prüfung auf null erfolgreich war, dann wird in Zeile 2 mit defaultImageRef.get() versucht, eine Strong Reference zu holen. Hier kann aber null geliefert werden, wenn zwischen der ersten und zweiten Zeile, der Garbage Collector alle SoftReference entfernt hat. Deswegen müssen wir den Code umschreiben:

public class SoftCachedImageProcessor {
    private static final String IMAGE_NAME = "bigImage.jpg";
    private SoftReference<InputStream> defaultImageRef = new SoftReference(loadImage());;

    public InputStream concatenateImegeWithDefaultVersion(InputStream userImageAsStream) {
        defaultImage = defaultImageRef.get();
        if (defaultImage == null) {
            defaultImage = this.getClass().getResourceAsStream(IMAGE_NAME);
            defaultImageRef = new SoftReference(defaultImage);
        }                
        // calculate and return concatenated image
    }            
}
  • broker broker,
  • 12 März 2013, 22:19
  • 2

Kommentare (0)

RSS zusammenklappen / ausklappen

Kommentar schreiben

Ihr Name
Sie sind ein Gast, Sie dürfen keine HTML-Tags verwenden
Bitte geben Sie die Zeichen in das folgende Feld ein