views:

285

answers:

3

I previously come to the conclusion that if you need a SoftReference with value (equals) based equality then one had a bad design, excepting an interner from this. This is following Google Collections and Guava not including such a class. But I've come across an issue that I think could use such an object.

We have an asset management system in a visual effects render farm with 100's of processes running the same job that only differ in the frame number it renders. We have an Oracle database that needs to record all the assets used. Instead of pounding Oracle with identical inserts where only one will succeed from all the jobs, in the middle-tier asset management system we can use a HashSet to record if the object that would be inserted into Oracle.

I could use a Google MapMaker with an expiration, but I don't want to have to worry about getting the expiration correct, we have renders that run in hours and some over days. Using a SoftReference with equals equality sounds like a much better way so the JVM will manage garbage collection automatically.

For other problems that I want to solve with a ConcurrentHashMap with garbage collection, I would use a strong reference in the HashMap as the key to get equals() equality and a SoftReference as the value so the JVM can garbage collect something, but in this case, the value doesn't matter and I don't have a value to wrap in a SoftReference to put there. So it seems like using a SoftReference with equals() would do the trick.

Any other suggestions on this?

A: 

In most cases when you want to use soft references with Google Collections, you should call

MapMaker.softValues()

With strong keys but soft values, lookups will use equality and key-value pairs will be garbage collected when memory is tight.

Jared Levy
But I don't have a value to associate with the key, so the key is the only thing that can be put in a soft reference.
Blair Zajac
A: 

I think that this class will meet your need:

import java.util.*;
import java.lang.ref.*;

public class SoftSet<T> extends AbstractSet<T> {

  private final WeakHashMap<T,SoftReference<T>> data = new WeakHashMap<T,SoftReference<T>>();

  public boolean add(T t) {
    return null == data.put(t, new SoftReference<T>(t));
  }

  public boolean remove(Object o) {
    return null != data.remove(o);
  }

  public boolean contains(Object o) {
    return data.containsKey(o);
  }

  public Iterator<T> iterator() {
    return data.keySet().iterator();
  }

  public int size() {
    return data.size();
  }

  public void clear() {
    data.clear();
  }

  public boolean removeAll(Collection<?> c) {
    return data.keySet().removeAll(c);
  }

  public boolean retainAll(Collection<?> c) {
    return data.keySet().retainAll(c);
  }
}

The way that this should work is that once the soft reference that is the value is cleared, then the value is weakly reachable only and the key can be removed from the inner map.

Geoff Reedy
why the downvote?
Geoff Reedy
Maybe the downvote because it's wrapping the object in two separate references than it needs to. Having a single SoftReference sublass with equals() equality in a Google ConcurrentHashMap may be cleaner this way.
Blair Zajac
+1  A: 

Since there is no ConcurrentHashSet using soft references, there are only two approaches:

1.) Your approach with the ConcurrentHashMap

  • Override equals and hashCode in the SoftReference
  • Inside of equals and hashCode only access the object using SoftReference#get
  • Put SoftReference as key, and any object as value (only null is not permitted)
  • If the Reference goes stale while accessing hashCode or equals, add the reference to a deletion queue to frequently remove the keys which are dead.
  • Check for contains via containsKey

2.) Use a ConcurrentMultimap<Integer, Set<SoftReference<RepLookupEntry>> and use hashCode as key, and a synchronized set of SoftReferences as values. When you get a hashCode hit, then check the contents of all SoftReferences for equality. Not very pretty, I agree and tricky to synchronize.

If I were in your position, I would not use SoftReferences at all, but rather a ConcurrentHashMap to keep strong references to your POJOs. Each time a new element arrives also put it in a ConcurrentLinkQueue. If the queue grows beyond a certain limit start removing elements from the HashMap.

Christopher Oezbek