1.1 --- a/openide.util/src/org/openide/util/Utilities.java Thu Oct 05 21:28:27 2006 +0000
1.2 +++ b/openide.util/src/org/openide/util/Utilities.java Thu Oct 05 23:43:28 2006 +0000
1.3 @@ -166,8 +166,7 @@
1.4 /** A height of the Mac OS X's menu */
1.5 private static final int TYPICAL_MACOSX_MENU_HEIGHT = 24;
1.6
1.7 - /** variable holding the activeReferenceQueue */
1.8 - private static ReferenceQueue<Object> activeReferenceQueue;
1.9 + private static ActiveQueue activeReferenceQueue;
1.10
1.11 /** reference to map that maps allowed key names to their values (String, Integer)
1.12 and reference to map for mapping of values to their names */
1.13 @@ -251,7 +250,9 @@
1.14 * <P>
1.15 * Do not call any <code>ReferenceQueue</code> methods. They
1.16 * will throw exceptions. You may only enqueue a reference.
1.17 - *
1.18 + * <p>
1.19 + * Be sure to call this method anew for each reference.
1.20 + * Do not attempt to cache the return value.
1.21 * @since 3.11
1.22 */
1.23 public static synchronized ReferenceQueue<Object> activeReferenceQueue() {
1.24 @@ -259,6 +260,8 @@
1.25 activeReferenceQueue = new ActiveQueue(false);
1.26 }
1.27
1.28 + activeReferenceQueue.ping();
1.29 +
1.30 return activeReferenceQueue;
1.31 }
1.32
1.33 @@ -2988,49 +2991,38 @@
1.34 /** Implementation of the active queue.
1.35 */
1.36 private static final class ActiveQueue extends ReferenceQueue<Object> implements Runnable {
1.37 - private boolean running;
1.38 +
1.39 + private static final Logger LOGGER = Logger.getLogger(ActiveQueue.class.getName().replace('$', '.'));
1.40 +
1.41 + /** number of known outstanding references */
1.42 + private int count;
1.43 private boolean deprecated;
1.44
1.45 public ActiveQueue(boolean deprecated) {
1.46 this.deprecated = deprecated;
1.47 -
1.48 - Thread t = new Thread(this, "Active Reference Queue Daemon"); // NOI18N
1.49 - t.setPriority(Thread.MIN_PRIORITY);
1.50 - t.setDaemon(true); // to not prevent exit of VM
1.51 - t.start();
1.52 }
1.53
1.54 public Reference<Object> poll() {
1.55 - throw new java.lang.UnsupportedOperationException();
1.56 + throw new UnsupportedOperationException();
1.57 }
1.58
1.59 public Reference<Object> remove(long timeout) throws IllegalArgumentException, InterruptedException {
1.60 - throw new java.lang.InterruptedException();
1.61 + throw new InterruptedException();
1.62 }
1.63
1.64 public Reference<Object> remove() throws InterruptedException {
1.65 - throw new java.lang.InterruptedException();
1.66 + throw new InterruptedException();
1.67 }
1.68
1.69 - /** Called either from Thread.run or RequestProcessor.post. In first case
1.70 - * calls scanTheQueue (only once) in the second and nexts calls cleanTheQueue
1.71 - */
1.72 public void run() {
1.73 - synchronized (this) {
1.74 - if (running) {
1.75 - return;
1.76 - }
1.77 -
1.78 - running = true;
1.79 - }
1.80 -
1.81 - for (;;) {
1.82 + while (true) {
1.83 try {
1.84 - Reference ref = super.remove(0);
1.85 + Reference<?> ref = super.remove(0);
1.86 + LOGGER.finer("dequeued reference");
1.87
1.88 if (!(ref instanceof Runnable)) {
1.89 - Logger.getAnonymousLogger().warning(
1.90 - "A reference not implementing runnable has been added to the Utilities.activeReferenceQueue (): " +
1.91 + LOGGER.warning(
1.92 + "A reference not implementing runnable has been added to the Utilities.activeReferenceQueue(): " +
1.93 ref.getClass() // NOI18N
1.94 );
1.95
1.96 @@ -3038,7 +3030,7 @@
1.97 }
1.98
1.99 if (deprecated) {
1.100 - Logger.getAnonymousLogger().warning(
1.101 + LOGGER.warning(
1.102 "Utilities.ACTIVE_REFERENCE_QUEUE has been deprecated for " + ref.getClass() +
1.103 " use Utilities.activeReferenceQueue" // NOI18N
1.104 );
1.105 @@ -3052,15 +3044,46 @@
1.106 } catch (Throwable t) {
1.107 // Should not happen.
1.108 // If it happens, it is a bug in client code, notify!
1.109 - Logger.getAnonymousLogger().log(Level.WARNING, null, t);
1.110 + LOGGER.log(Level.WARNING, null, t);
1.111 } finally {
1.112 // to allow GC
1.113 ref = null;
1.114 }
1.115 } catch (InterruptedException ex) {
1.116 - Logger.getAnonymousLogger().log(Level.WARNING, null, ex);
1.117 + LOGGER.log(Level.WARNING, null, ex);
1.118 + }
1.119 +
1.120 + synchronized (this) {
1.121 + assert count > 0;
1.122 + count--;
1.123 + if (count == 0) {
1.124 + // We have processed all we have to process (for now at least).
1.125 + // Could be restarted later if ping() called again.
1.126 + // This could also happen in case someone called activeReferenceQueue() once and tried
1.127 + // to use it for several references; in that case run() might never be called on
1.128 + // the later ones to be collected. Can't really protect against that situation.
1.129 + // See issue #86625 for details.
1.130 + LOGGER.fine("stopping thread");
1.131 + break;
1.132 + }
1.133 }
1.134 }
1.135 }
1.136 +
1.137 + synchronized void ping() {
1.138 + if (count == 0) {
1.139 + Thread t = new Thread(this, "Active Reference Queue Daemon"); // NOI18N
1.140 + t.setPriority(Thread.MIN_PRIORITY);
1.141 + t.setDaemon(true); // to not prevent exit of VM
1.142 + t.start();
1.143 + // Note that this will not be printed during IDE startup because
1.144 + // it happens before logging is even initialized.
1.145 + LOGGER.fine("starting thread");
1.146 + } else {
1.147 + LOGGER.finer("enqueuing reference");
1.148 + }
1.149 + count++;
1.150 + }
1.151 +
1.152 }
1.153 }
2.1 --- a/openide.util/test/unit/src/org/openide/util/UtilitiesActiveQueueTest.java Thu Oct 05 21:28:27 2006 +0000
2.2 +++ b/openide.util/test/unit/src/org/openide/util/UtilitiesActiveQueueTest.java Thu Oct 05 23:43:28 2006 +0000
2.3 @@ -19,22 +19,19 @@
2.4
2.5 package org.openide.util;
2.6
2.7 -import java.lang.ref.*;
2.8 -
2.9 -import junit.framework.*;
2.10 -
2.11 -import org.netbeans.junit.*;
2.12 +import java.lang.ref.Reference;
2.13 +import java.lang.ref.ReferenceQueue;
2.14 +import java.lang.ref.WeakReference;
2.15 +import java.net.URL;
2.16 +import java.net.URLClassLoader;
2.17 +import org.netbeans.junit.NbTestCase;
2.18
2.19 public class UtilitiesActiveQueueTest extends NbTestCase {
2.20
2.21 - public UtilitiesActiveQueueTest(java.lang.String testName) {
2.22 + public UtilitiesActiveQueueTest(String testName) {
2.23 super(testName);
2.24 }
2.25
2.26 - public static void main(java.lang.String[] args) {
2.27 - junit.textui.TestRunner.run(new NbTestSuite(UtilitiesActiveQueueTest.class));
2.28 - }
2.29 -
2.30 public void testRunnableReferenceIsExecuted () throws Exception {
2.31 Object obj = new Object ();
2.32 RunnableRef ref = new RunnableRef (obj);
2.33 @@ -95,15 +92,60 @@
2.34 }
2.35 }
2.36
2.37 + public void testMemoryLeak() throws Exception {
2.38 + final Class<?> u1 = Utilities.class;
2.39 + class L extends URLClassLoader {
2.40 + public L() {
2.41 + super(new URL[] {u1.getProtectionDomain().getCodeSource().getLocation()}, u1.getClassLoader().getParent());
2.42 + }
2.43 + protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
2.44 + if (name.equals(u1.getName()) || name.startsWith(u1.getName() + "$")) {
2.45 + Class c = findLoadedClass(name);
2.46 + if (c == null) {
2.47 + c = findClass(name);
2.48 + }
2.49 + if (resolve) {
2.50 + resolveClass(c);
2.51 + }
2.52 + return c;
2.53 + } else {
2.54 + return super.loadClass(name, resolve);
2.55 + }
2.56 + }
2.57 + }
2.58 + ClassLoader l = new L();
2.59 + Class<?> u2 = l.loadClass(u1.getName());
2.60 + assertEquals(l, u2.getClassLoader());
2.61 + Object obj = new Object();
2.62 + @SuppressWarnings("unchecked")
2.63 + ReferenceQueue<Object> q = (ReferenceQueue<Object>) u2.getMethod("activeReferenceQueue").invoke(null);
2.64 + RunnableRef ref = new RunnableRef(obj, q);
2.65 + synchronized (ref) {
2.66 + obj = null;
2.67 + assertGC("Ref should be GC'ed as usual", ref);
2.68 + ref.wait();
2.69 + assertTrue("Run method has been executed", ref.executed);
2.70 + }
2.71 + Reference<?> r = new WeakReference<Object>(u2);
2.72 + q = null;
2.73 + u2 = null;
2.74 + l = null;
2.75 + assertGC("#86625: Utilities.class can also be collected now", r);
2.76 + }
2.77 +
2.78
2.79 - private static class RunnableRef extends WeakReference
2.80 + private static class RunnableRef extends WeakReference<Object>
2.81 implements Runnable {
2.82 public boolean wait;
2.83 public boolean entered;
2.84 public boolean executed;
2.85
2.86 public RunnableRef (Object o) {
2.87 - super (o, Utilities.activeReferenceQueue ());
2.88 + this(o, Utilities.activeReferenceQueue());
2.89 + }
2.90 +
2.91 + public RunnableRef(Object o, ReferenceQueue<Object> q) {
2.92 + super(o, q);
2.93 }
2.94
2.95 public synchronized void run () {