Question

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.

Était-ce utile?

La solution

The traditional way to respond to garbage collection events is to register the WeakReferences with a ReferenceQueue, in which they'll automatically be enqueued when the referenced object is GC'd, and then to periodically poll the ReferenceQueue (possibly in a separate thread) to do cleanups.

A standard trick is to extend the WeakReference class, attaching any additional information you want to know when the cleanup occurs, and then to cast the WeakReference objects in the ReferenceQueue back to your MyWeakReference to get the information.

Autres conseils

An somewhat simpler alternative that might give quicker results (and that also alleviates the potential bottleneck on Savings) is

private final ConcurrentMap<WeakReference, Integer> savings = new ConcurrentHashMap<>();

public void run() {
    while(running) {
        for(WeakReference reference : savings.keySet()) {
            if(reference.get() == null) {
                log(savings.remove(reference));
            }
        }
        sleep(1000);
    }
}

The downside is that you must continually iterate through the map in order to find cleared references, but the advantage is a simpler implementation, and reference.get() == null will be true as soon as the object is cleared (whereas there may be a delay in registering a cleared object in a WeakHashMap). Using a ConcurrentMap alleviates the bottleneck that may be created by your use of a Collections.synchronizedMap, and more importantly prevents the for-each loop from throwing a ConcurrentModificationException.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top