codestory

Die Anleitung zu Java SoftReference

  1. SoftReference
  2. SoftReference(T, ReferenceQueue<? super T>
  3. Caching example

1. SoftReference

Die Klasse java.lang.ref.SoftReference wird verwendet, um ein Objekt zu erstellen, das ein anderes Objekt umschließt - innerObject. Das umschlossene Objekt kann vom Garbage Collector (GC) aus dem Speicher entfernt werden, wenn es nicht mehr an einem anderen Ort verwendet wird, der stärker als GC ist und das System mehr Speicher benötigt.
Das Objekt ist in eine WeakReference eingebettet, die sich wie ein Diner in einem Restaurant verhält. Wenn die Gäste mit dem Essen fertig sind, sind sie bereit, den Tisch zu verlassen, auch wenn das Restaurant zu dieser Zeit viele leere Tische hat. SoftReference unterscheidet sich ein wenig von WeakReference, Gäste können sich zurücklehnen und nur gehen, wenn das Restaurant keine freien Tische mehr hat oder die Anzahl der verfügbaren freien Tische unter einem sicheren Wert liegt.
Grundsätzlich ist es etwas schwieriger, ein Beispiel für SoftReference zu geben und Ihnen aus erster Hand zu zeigen, wie Garbage Collector weiche Referenzen aus dem Speicher entfernt, da wir eine Situation mit nahezu vollem Speicher simulieren müssen. Wenn die Simulation nicht perfekt ist, wird der Speicher überlaufen. Obwohl vor dem Auslösen von OutOfMemoryError weiche Verweise aus dem Speicher entfernt wurden.
Hinweis: WeakReference sollte erlernt werden, bevor Sie mit SoftReference fortfahren:
Wenn ein Objekt aus dem Speicher entfernt wird, wird seine Methode finalize() aufgerufen. Hinweis: Diese Methode wurde seit Java 9 als veraltet markiert, wir können sie jedoch weiterhin nur zum Drucken einer Nachricht verwenden.
AnyObject.java
package org.o7planning.beans;
 
public class AnyObject {
    private String val;
    public AnyObject(String val) {
        this.val = val;
    }
    public String getVal() {
        return this.val;
    }
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("I am being removed from memory");
    }
}
Das folgende Beispiel zeigt, dass GC weiche Verweise aus dem Speicher entfernt, um das Auslösen eines OutOfMemoryErrorr zu vermeiden.
SoftReference_obj_ex1.java
package org.o7planning.softreference.ex;

import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;

import org.o7planning.beans.AnyObject;

public class SoftReference_obj_ex1 {

    public static void main(String[] args) throws InterruptedException {
        // Create myAnyObject reference points to AnyObject("Obj1").
        AnyObject myAnyObject = new AnyObject("Obj1");

        // Create SoftReference object
        SoftReference<AnyObject> softRef = new SoftReference<AnyObject>(myAnyObject);

        System.out.println("softRef.get(): " + softRef.get());

        List<String> list= new ArrayList<String>();

        int i = 0;
        String s = "";
        while (true) {
            AnyObject innerObject = softRef.get();
            if (innerObject == null) {
                System.out.println("Inner object is removed by Garbage Collector");
                System.out.println("softRef.get(): " + innerObject);
                break;
            }
            i++;
            //
            s = s + " String " + i; // Throw OutOfMemoryError
            list.add(s);
            System.out.println("Create new String: " + i);
        }
    }
}
Output:
softRef.get(): org.o7planning.beans.AnyObject@5e91993f
Create new String: 1
Create new String: 2

...

Create new String: 24952
Create new String: 24953
Create new String: 24954
Exception in thread "main" I am being removed from memory
java.lang.OutOfMemoryError: Java heap space
    at java.base/java.util.Arrays.copyOfRange(Arrays.java:4030)
    at java.base/java.lang.StringLatin1.newString(StringLatin1.java:715)
    at java.base/java.lang.StringBuilder.toString(StringBuilder.java:448)
    at org.o7planning.softreference.ex.SoftReference_obj_ex1.main(SoftReference_obj_ex1.java:33)
Java ermöglicht Ihnen zu konfigurieren, wann GC Soft-Referenzen aus dem Speicher entfernen soll.
-XX:SoftRefLRUPolicyMSPerMB=1000
Der Standardwert des Parameters SoftRefLRUPolicyMSPerMB beträgt 1000 Millisekunden. Dies bedeutet, dass wenn nur der Speicher HEAP nur 10MB verfügbar sind, GC wird Soft-Referenzen entfernt, die länger als 1000 Millisekunden nicht verwendet wurden.
Die Dokumentation Java sagt, dass:
"All soft references to softly-reachable objects are guaranteed to have been freed before the virtual machine throws an OutOfMemoryError." - Alle Soft-Referenzen auf weich erreichbare Objekte werden garantiert freigegeben, bevor die virtuelle Maschine einen OutOfMemoryError auslöst.
Anscheinend ist das obige nur dann wirklich wahr, wenn "-XX:SoftRefLRUPolicyMSPerMB=0".
Noch ein besseres Beispiel. Versuchen wir, eine Situation zu simulieren, in der den Speicher von Java fast erschöpft ist:
SoftReference_obj_ex2.java
package org.o7planning.softreference.ex;

import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;

import org.o7planning.beans.AnyObject;

public class SoftReference_obj_ex2 {

    public static void main(String[] args) throws InterruptedException {
        // A String has size of 1Mb.
        String oneMbString = create1MbString();

        // Create myAnyObject (~2 Mb in HEAP).
        AnyObject myAnyObject = new AnyObject(oneMbString + oneMbString);

        // Create SoftReference object
        SoftReference<AnyObject> softRef = new SoftReference<AnyObject>(myAnyObject);

        System.out.println("softRef.get(): " + softRef.get());
        myAnyObject = null;

        List<String> list = new ArrayList<String>();

        int i = 0;

        while (true) {
            i++;
            long freeMemoryInBytes = Runtime.getRuntime().freeMemory(); // in bytes
            long freeMemoryInMbs = freeMemoryInBytes / (1024 * 1024);
            System.out.println("Free memory in Mb: " + freeMemoryInMbs);

            //
            if (freeMemoryInMbs <= 10) {
                Thread.sleep(1200);
            }
            if (freeMemoryInMbs <= 2) {
                System.out.println("Done!");
                break;
            }
            System.out.println(" >> Create new String");
            String s = oneMbString + " - " + i;
            list.add(s);
        }
    }

    // Create a String has the size of 1MB.
    // 1MB = 1024KB = 1024x1024 bytes = (2^10)*(2^10) bytes = 2^20 bytes.
    private static String create1MbString() {
        String s = "A"; // 2 bytes
        for (int i = 0; i < 20; i++) {
            s = s + s;
        }
        return s;
    }
}
Output:
softRef.get(): org.o7planning.beans.AnyObject@5e91993f
Free memory in Mb: 238
 >> Create new String

...

Free memory in Mb: 16
>> Create new String
Free memory in Mb: 12
>> Create new String
Free memory in Mb: 8
>> Create new String
Free memory in Mb: 14
>> Create new String
Free memory in Mb: 10
>> Create new String
Free memory in Mb: 10
>> Create new String
Free memory in Mb: 8
>> Create new String
Free memory in Mb: 6
>> Create new String
Free memory in Mb: 4
I am being removed from memory
>> Create new String
Free memory in Mb: 2
Done!
SoftReference constructors
SoftReference(T referent)

SoftReference(T referent, ReferenceQueue<? super T> queue)
Alle Methoden von SoftReference werden von der Elternklasse geerbt.
// Methods inherited from parent.
public T get()  
public void clear()   
public boolean isEnqueued()   
public boolean enqueue()

2. SoftReference(T, ReferenceQueue<? super T>

Erstellen Sie ein Objekt SoftReference, das das Objekt innerObject umschließt. Wenn innerObject von GC aus dem Speicher entfernt wird, wird dieses Objekt SoftReference der queue hinzugefügt.
SoftReference(T innerObject, ReferenceQueue<? super T> queue)

3. Caching example

Manchmal wird SoftReference auch in einem einfachen System cache verwendet, in dem sich die Daten selten ändern. Die Virtual Machine Java entfernt diese Daten automatisch, wenn sie mehr Arbeitsspeicher benötigt.
Beispiel: Ein System cache speichert Daten von Bilddateien
ImageCache.java
package org.o7planning.softreference.cache.ex;

import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;

public class ImageCache {
    private static final ImageCache instance = new ImageCache();

    private Map<String, SoftReference<byte[]>> cacheMap = new HashMap<>();

    private ImageCache() {
    }

    public static ImageCache getInstance() {
        return instance;
    }

    public byte[] getImageData(String imagePath) {
        SoftReference<byte[]> value = this.cacheMap.get(imagePath);
        
        byte[] data = null;
        if(value == null || (data = value.get()) == null)  {
            data = this.readImageFromDisk(imagePath);
            this.cacheMap.put(imagePath, new SoftReference<byte[]>(data));
            System.out.println(">> Load data from disk: " + imagePath);
        } else  {  
            System.out.println("   Found data in cache: " + imagePath);
        }  
        return data;
    }

    private byte[] readImageFromDisk(String imagePath) {
        // Read from disk..
        return new byte[3];
    }
}
ImageCacheTest.java
package org.o7planning.softreference.cache.ex;

public class ImageCacheTest {

    public static void main(String[] args) throws InterruptedException {
        String[] imagePaths = new String[] { //
                "image1.png", //
                "image1.png", //
                "image2.png", //
                "image2.png", //
                "image1.png", //
                "image3.png", //
                "image3.png", //
                "image1.png", //
        };

        ImageCache cache = ImageCache.getInstance();

        for (int i = 0; i < imagePaths.length; i++) {
            byte[] data = cache.getImageData(imagePaths[i]);
            Thread.sleep(500);
        }

        System.out.println("Done!");
    }
}
Output:
>> Load data from disk: image1.png
   Found data in cache: image1.png
>> Load data from disk: image2.png
   Found data in cache: image2.png
   Found data in cache: image1.png
>> Load data from disk: image3.png
   Found data in cache: image3.png
   Found data in cache: image1.png
Done!

Java Grundlagen

Show More