Object cache

I am creating an awful amount of Point objects (my own Point object, not the standard one) to store selected points in a 3D space in hashmaps/lists. I realized that I might be getting an unhealthy number of instances of the same Point object (this is actually a real problem thanks to mass selection + storing changes to allow for undoing/redoing), so I implemented a cache based on a simple HashMap that stored Point instances. However, I also wanted the cache to clear Points that aren’t in use anywhere, so I added a reference counter to the Point class so I can keep track on if it’s okay to remove them completely from the HashMap. This turned out to be more messy than I thought since I have to be sure not to screw up the count manually every time I do something with them. Is there any way of automating this? I basically want instances of Point that only exist in the PointCache and aren’t used anywhere else to be automatically garbage collected and removed from the PointCache. It’s a bit similar to how a daemon thread works. I want my Point objects to be garbage collected when they only exist in the PointCache.

Well, you can always use a Bag system.

But, given the huge amount of points you are creating, it might be a better idea to make two single integer arrays (one for x and one for y) and add/remove the points from there. It should be a lot quicker, unless you really want to use OOP for this task.

WeakHashMap ! (be sure to read the javadoc, values are not stored as weak references!)

Use Guava’s CacheBuilder and you can have weak keys, weak values, both, expiration, eviction policies, statistics, and so on and so forth. Extremely spiffy stuff, I use it as a fast duplicate eliminator in my apps in front of Ehcache.

@theagentd - how are you retrieving the Point instances? Creating a Point, seeing if it already exists and discarding the new one? If so, using WeakHashMap basically as a WeakHashSet could work - seen a few implementations of this that just put Boolean.TRUE as every value.

If you want to create from raw coordinates, you could maybe look at something like an IntHashMap with the keys being the hash of the Point object. The values are PointReferences (or a list if you can’t guarantee no hash collision), which extends WeakReference and includes an int field for the hashcode, so that the reference can be removed on collection. It’s similar to the Entry inner class in the WeakHashMap.

Probably OT, but you missed my favourite feature here, and one I wish was in WeakHashMap - the ability to listen for evictions and recycle or dispose the value objects - thinking particularly for use in a texture cache.

Here’s my full PointCache class:


import java.util.HashMap;

public class PointCache {
	
	private static HashMap<Long, Point> cache = new HashMap<>();
	
	private PointCache(){}
	
	public static Point get(int x, int y, int z){
		long key = ((long)x << 48) | ((long)y << 24) | z;

		Point p = cache.get(key);
		if(p == null){
			p = new Point(x, y, z);
			cache.put(key, p);
		}
		
		p.incRefCount();
		return p;
	}

   //Called by Point when the reference count becomes 0
	protected static void remove(Point p) {
		long key = ((long)p.x() << 48) | ((long)p.y() << 24) | p.z();
		cache.remove(key);
	}
}

That’s a big problem in my case. Since I use autoboxed Long values, the values that aren’t cached by the VM will be immediately reclaimed by the GC. I want the values to be weak references and the whole entry (key + value) to be removed when the value gets reclaimed.

This seems a bit overkill. The objects aren’t expensive at all to create. I just want to minimize memory usage as much as possible, so the overhead of something as advanced might defeat the purpose of it all. Thanks anyway, seems like that cache builder might come in handy sometime.

@nsigma
The key I generate now assures me that I have no collisions, though I don’t think it’s a very good hash code (performance-wise). It’s quick to generate though. The key does not fit in an int.

Maybe the simplest way is just to use a normal HashMap and wrap the Points in WeakReferences. Sadly it seems like WeakReferences are pretty heavyweight and contain lots of stuff, so simply the overhead of the WeakReferences might defeat the purpose of it all. I’d also have to periodically clear all dead WeakReferences from the hash map too…

Well, quick Google for a LongHashMap found this in JogAmp - http://jogamp.org/deployment/v2.0-rc6/javadoc/gluegen/javadoc/com/jogamp/common/util/LongObjectHashMap.html Get rid of the need to autobox, and should be optimised for the data type.

For the values create a PointReference class as I suggested. ie.


class PointReference extends WeakReference<Point> {

  long key;

  PointReference(Point point, ReferenceQueue<Point> queue, long key) {
    super(point, queue);
    this.key = key;
    
    // etc...


That way you drain the queue periodically, grabbing the keys from the references and remove them from the map - then you don’t need to iterate through looking for dead references.

Definitely one to benchmark, though. You might be right about the overhead.

EDIT: If this is purely about memory overhead, you should probably also consider the PointReference extending SoftReference if it’s likely that you’ll want the same points again.

Ah! ReferenceQueues!

This seems reasonable. Performance is not my main concern here. Soft references might be more what I want then.

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;

/**
 * This class is not thread safe (but neither is the caller in my case)
 */
public class PointCache {
	
	private static HashMap<Long, PointReference> cache = new HashMap<>();
	private static ReferenceQueue<Point> queue = new ReferenceQueue<>();

	public static Point get(int x, int y, int z){
		Long key = getKey(x, y, z);
		PointReference pr = cache.get(key);
		if(pr != null){
			Point p = pr.get();
			if(p != null){
				return p;
			}
		}
		Point p = new Point(x, y, z);
		cache.put(key, new PointReference(p, queue, key));
		return p;
	}
	
	public static void clearDeadReferences(){
		Reference<? extends Point> r;
		while((r = queue.poll()) != null){
			PointReference pr = (PointReference) r;
			cache.remove(pr.getKey());
			System.out.println("Removed an entry from the point cache.");
		}
	}
	
	private static Long getKey(int x, int y, int z){
		return ((long)x << 48) | ((long)y << 24) | z;
	}

	private static class PointReference extends WeakReference<Point>{
		
		private Long key;

		public PointReference(Point p, ReferenceQueue<Point> queue, Long key) {
			super(p, queue);
			this.key = key;
		}
		
		public Long getKey(){
			return key;
		}
	}
}

Tried this code, output:

Does exactly what I want. I just have to change it to a SoftReference instead to make it more cachy. =S

Thanks everyone! This is a LOT simpler than keeping track of the reference count. Let’s hope it works out performance wise too. =S