2 // ========================================================================
3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.util;
21 import java.lang.ref.PhantomReference;
22 import java.lang.ref.ReferenceQueue;
23 import java.lang.ref.WeakReference;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
27 import org.eclipse.jetty.util.component.AbstractLifeCycle;
28 import org.eclipse.jetty.util.log.Log;
29 import org.eclipse.jetty.util.log.Logger;
32 * A facility to detect improper usage of resource pools.
34 * Resource pools usually have a method to acquire a pooled resource
35 * and a method to released it back to the pool.
37 * To detect if client code acquires a resource but never releases it,
38 * the resource pool can be modified to use a {@link LeakDetector}.
39 * The modified resource pool should call {@link #acquired(Object)} every time
40 * the method to acquire a resource is called, and {@link #released(Object)}
41 * every time the method to release the resource is called.
42 * {@link LeakDetector} keeps track of these resources and invokes method
43 * {@link #leaked(org.eclipse.jetty.util.LeakDetector.LeakInfo)} when it detects that a resource
44 * has been leaked (that is, acquired but never released).
46 * To detect whether client code releases a resource without having
47 * acquired it, the resource pool can be modified to check the return value
48 * of {@link #released(Object)}: if false, it means that the resource was
51 * IMPLEMENTATION NOTES
53 * This class relies on {@link System#identityHashCode(Object)} to create
54 * a unique id for each resource passed to {@link #acquired(Object)} and
55 * {@link #released(Object)}. {@link System#identityHashCode(Object)} does
56 * not guarantee that it will not generate the same number for different
57 * objects, but in practice the chance of collision is rare.
59 * {@link LeakDetector} uses {@link PhantomReference}s to detect leaks.
60 * {@link PhantomReference}s are enqueued in their {@link ReferenceQueue}
61 * <em>after</em> they have been garbage collected (differently from
62 * {@link WeakReference}s that are enqueued <em>before</em>).
63 * Since the resource is now garbage collected, {@link LeakDetector} checks
64 * whether it has been released and if not, it reports a leak.
65 * Using {@link PhantomReference}s is better than overriding {@link #finalize()}
66 * and works also in those cases where {@link #finalize()} is not
69 * @param <T> the resource type.
71 public class LeakDetector<T> extends AbstractLifeCycle implements Runnable
73 private static final Logger LOG = Log.getLogger(LeakDetector.class);
75 private final ReferenceQueue<T> queue = new ReferenceQueue<>();
76 private final ConcurrentMap<String, LeakInfo> resources = new ConcurrentHashMap<>();
77 private Thread thread;
80 * Tracks the resource as been acquired.
82 * @param resource the resource that has been acquired
83 * @return whether the resource has been tracked
84 * @see #released(Object)
86 public boolean acquired(T resource)
88 String id = id(resource);
89 return resources.putIfAbsent(id, new LeakInfo(resource, id)) == null;
93 * Tracks the resource as been released.
95 * @param resource the resource that has been released
96 * @return whether the resource has been acquired
97 * @see #acquired(Object)
99 public boolean released(T resource)
101 String id = id(resource);
102 return resources.remove(id) != null;
106 * Generates a unique ID for the given resource.
108 * @param resource the resource to generate the unique ID for
109 * @return the unique ID of the given resource
111 protected String id(T resource)
113 return String.valueOf(System.identityHashCode(resource));
117 protected void doStart() throws Exception
120 thread = new Thread(this, getClass().getSimpleName());
121 thread.setDaemon(true);
126 protected void doStop() throws Exception
139 @SuppressWarnings("unchecked")
140 LeakInfo leakInfo = (LeakInfo)queue.remove();
141 LOG.debug("Resource GC'ed: {}", leakInfo);
142 if (resources.remove(leakInfo.id) != null)
146 catch (InterruptedException x)
153 * Callback method invoked by {@link LeakDetector} when it detects that a resource has been leaked.
155 * @param leakInfo the information about the leak
157 protected void leaked(LeakInfo leakInfo)
159 LOG.warn("Resource leaked: " + leakInfo.description, leakInfo.stackFrames);
163 * Information about the leak of a resource.
165 public class LeakInfo extends PhantomReference<T>
167 private final String id;
168 private final String description;
169 private final Throwable stackFrames;
171 private LeakInfo(T referent, String id)
173 super(referent, queue);
175 this.description = referent.toString();
176 this.stackFrames = new Throwable();
180 * @return the resource description as provided by the resource's {@link Object#toString()} method.
182 public String getResourceDescription()
188 * @return a Throwable instance that contains the stack frames at the time of resource acquisition.
190 public Throwable getStackFrames()
196 public String toString()