My application logs the usage of certain objects - my setup uses AspectJ to identify the contexts I'm interested in and logs these usages. I later load the log files for analysis, but for efficiency reasons it's useful to know when an object is no longer reachable.
My current approach is to log the objects I'm interested in with a 'garbage logger', which then creates a 'saver' object containing the object's identity hash code and stores this in a weak hash map. The idea being that when the object is collected, the saver object will be removed from the weak hash map and collected, thus running code to log the identity hash code of the collected object. I use a separate thread and queue to prevent causing a bottleneck in the garbage collector. Here's the garbage logger code:
public class GarbageLogger extends Thread {
private final Map<Object,Saver> Savings =
Collections.synchronizedMap(new WeakIdentityHashMap<Object,Saver>());
private final ConcurrentLinkedQueue<Integer> clearTheseHash =
new ConcurrentLinkedQueue<Integer>();
public void register(Object o){
Savings.put(o,new Saver(System.identityHashCode(o));
}
private class Saver{
public Saver(int hash){ this.hash=hash;}
private final int hash;
@Override
public void finalize(){
clearTheseHash.add(hash);
}
}
@Override
public void run(){
while(running){
if((clearTheseHash.peek() !=null)){
int h = clearTheseHash.poll();
log(h);
}
else sleep(100);
}
}
// logging and start/end code omitted
}
My problem is that this seems very convoluted and, because weak hash map won't necessarily clear its entries unless space is needed, I might be waiting a long time after the object is collected before recording it. Basically, I'm looking for a better way to achieve this.
Note - I am monitoring arbitrary objects and have no control over their creation so cannot override their finalize methods.