eclipseeclipse-matmat

how to dig into this memory leak with eclipse MAT further


I have an issue where a ScheduledThreadPoolExecutor ends up with 3 million future tasks. I am trying to see what type of task so I can go to where that task is scheduled, but I am not sure how to get any info from this screen(I have tried right clicking those future tasks and selecting various choices in the menu). It seems like there is something missing in the gui like the links to the actual runnables or something...

any ideas on how to drill into further?

enter image description here


Solution

  • Some General Stuff

    You need to know, that if you have a portable heap dump (phd, see types here), then it does not contain actual data (primitives), so then you can make your findings only based on reference map (which types hold a reference to which other types).

    You can give a try to OQL. This is an SQL like language, with which you can query your objects.

    One example:

    select * from java.lang.String s where s.@retainedHeapSize>10000
    

    This gives back all strings, that are bigger than ~10k. You can make also some functions (like this aggregating here).

    You could give a try to it.

    As for the current problem

    If you check the FutureTask source (here is JDK6 below):

    public class FutureTask<V> implements RunnableFuture<V> {
        /** Synchronization control for FutureTask */
        private final Sync sync;
    
    ...
        public FutureTask(Callable<V> callable) {
            if (callable == null)
                throw new NullPointerException();
            sync = new Sync(callable);
        }
    ...
    
        public FutureTask(Runnable runnable, V result) {
            sync = new Sync(Executors.callable(runnable, result));
        }
    

    The actual Runnable is referred by the Sync object:

      private final class Sync extends AbstractQueuedSynchronizer {
            private static final long serialVersionUID = -7828117401763700385L;
    
            /** State value representing that task is running */
            private static final int RUNNING   = 1;
            /** State value representing that task ran */
            private static final int RAN       = 2;
            /** State value representing that task was cancelled */
            private static final int CANCELLED = 4;
    
            /** The underlying callable */
            private final Callable<V> callable;
            /** The result to return from get() */
            private V result;
            /** The exception to throw from get() */
            private Throwable exception;
    
            /**
             * The thread running task. When nulled after set/cancel, this
             * indicates that the results are accessible.  Must be
             * volatile, to ensure visibility upon completion.
             */
            private volatile Thread runner;
    
            Sync(Callable<V> callable) {
                this.callable = callable;
            }
    

    So in the GUI open the Sync object (not open in your picture), and then you can check the Runnables.

    I dont know if you can change the code or not, but in general it is better always limit the size of the queue used by an executor, since this way you can avoid leaks. Or you can use some persisted queue. If you apply a limit you can define the rejection policy like for example reject, run in caller and so on. See http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ThreadPoolExecutor.html for details.