Reputation: 1082
I have a
ConcurrentMap<String, SoftReference<X>> map = new ConcurrentHashMap<String, SoftReference<X>>();
and would like the key/value pair removed from the map when the referent of the SoftReference is GC'ed.
How do I go about achieving this?
Upvotes: 0
Views: 583
Reputation: 16158
SoftReference
object from Map will not be garbage collected until you remove it from map and no reference is pointing to that object. First remove <K,V>
pair from map then it will be automatically garbage collected.
Upvotes: 0
Reputation: 373
Key/value pair will not be removed, when the referent of the SoftReference is GC'ed. THe following program shows the result.
package test.gc;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
public class TestSoftReference {
public static ReferenceQueue<TestModel> referenceQueue = new ReferenceQueue<>();
public static ConcurrentHashMap<Integer, SoftReference<TestModel>> map = new ConcurrentHashMap<>();
public static void main(String[] args) throws InterruptedException {
final int KEY1 = 1;
TestModel testModel1 = new TestModel(KEY1);
SoftReference<TestModel> reference1 = new SoftReference<TestModel>(testModel1, referenceQueue);
map.put(testModel1.getNumber(), reference1);
testModel1 = null;
while (true) {
Object obj = referenceQueue.poll();
if (obj != null) {
System.out.println("queue.poll at " + new Date() + " " + obj);
break;
}
System.gc();
}
SoftReference<TestModel> tempReference = null;
tempReference = map.get(KEY1);
System.err.println("reference:" + tempReference);
if (tempReference != null) {
System.err.println("referent:" + tempReference.get());
}
}
}
class TestModel {
private int number = 0;
private int[] bigArray = null;
public int getNumber() {
return number;
}
public TestModel(int n) {
if (n <= 0) {
throw new IllegalArgumentException("argument n must greater than 0.");
} else {
number = n;
bigArray = new int[n * 1024 * 1024];
}
}
@Override
public String toString() {
return "field bigArray's baseNumber is " + number + " and bigArray's length is " + bigArray.length;
}
}
And the result is:
queue.poll at Thu Jul 26 14:37:21 CST 2012 java.lang.ref.SoftReference@1c501f7
reference:java.lang.ref.SoftReference@1c501f7
referent:null
As it shows, the key/value pair is not removed, when the refent is GC'ed.
You can implement your own RefenceQueue and SoftReference to achive your target, and override RefenceQueue's poll method, in which you can do your own job, like this:
class SoftReferenceMonitor extends ReferenceQueue<TestModel> {
@Override
public Reference<? extends TestModel> poll() {
@SuppressWarnings("unchecked")
Reference<TestModel> ref = (Reference<TestModel>) super.poll();
if (ref != null) {
int id = ((CustomizedSoftReference)ref).getId();
TestSoftReference.map.remove(id);
System.err.println("remove key/value '" + id + "' from map.");
}
return ref;
}
}
class CustomizedSoftReference extends SoftReference<TestModel> {
private int id;
public int getId() {
return id;
}
public CustomizedSoftReference(TestModel referent, ReferenceQueue<? super TestModel> q) {
super(referent, q);
this.id = referent.getNumber();
}
}
Then replace SoftReference and SoftReferenceMonitor with SoftReferenceMonitor and CustomizedSoftReference respectively, and run the program. The result is:
remove key/value '1' from map.
reference:null
queue.poll at Thu Jul 26 14:46:54 CST 2012 test.gc.CustomizedSoftReference@4e7958
Now the key/value pair has been removed.
Besides, you can use another thread to construct the SoftReferenceMonitor instance and monitor the SoftReference.
Hope this useful.
Upvotes: 1
Reputation: 2191
Did you considered guava cache ?
It will handle for you this aspect.
If you really can't use this library, maybe you will have to override the concurrentHashMap to test if the softreference still hold something, and remove the entry in that case
Upvotes: 0