Benefits of activeReferenceQueue

Jaroslav Tulach jaroslav.tulach at oracle.com
Tue Jul 29 09:06:56 UTC 2014


Dne Út 29. července 2014 18:16:24, David Holmes napsal(a):
> Thanks for the detailed explanation. It is an interesting problem. 

Good. I am glad it attracted your attention. I am also glad Peter came with 
his static method...

> On 29/07/2014 6:03 PM, Peter Levart wrote:
> > On 07/29/2014 04:16 AM, David Holmes wrote:
> >> Hi Jaroslav,
> >> 
> >> So ... activeReferenceQueue is a reference queue that embodies a
> >> thread that does the polling and implements a psuedo-finalization
> >> mechanism. This works fine in the normal case where the lifetime of
> >> the queue is the lifetime of the "application". In the WAR case (and I
> >> don't know the details of WAR deployment) each time it is deployed in
> >> the same VM we get a new activeReferenceQueue and a new thread.
> >> 
> >> The basic issue is that the thread has a strong reference to the queue
> >> and has no idea when it should exit and so the thread and queue remain
> >> forever even if there is no user code with a reference to the queue -
> >> does that sum it up?
> >> 
> >> Can the thread not hold a weakreference to the queue and poll using
> >> remove(timeout) and then terminate when the queue reference is gone?
> >> 
> >> Thanks,
> >> David
> > 
> > The main problem I think is, that when calling queue.remove(timeout) the
> > thread *does* hold a strong reference to the queue for the entire time
> > it waits for something to be enqueued. So majority of time in a loop,
> > thread holds a strong reference to queue preventing it from being
> > considered for WeakReference processing or at least prolonging it's life
> > into an indefinite future (the window of opportunity for queue to be
> > found weakly reachable is very small).
> > 
> > That's why Jaroslav is hacking with reflection to get hold of the 'lock'
> > so that he can re-implement the ReferenceQueue.remove(timeout) method
> > without holding a strong reference to the queue. If we wanted to make
> > Jaroslav's life easier, a method like the following in ReferenceQueue
> > could help him:
> > 
> > 
> > public class ReferenceQueue {
> > 
> >      ...
> >      
> >      public static class CollectedException extends Exception {}
> >      
> >      public static <T> Reference<? extends T> remove(
> >      
> >          Reference<? extends ReferenceQueue<T>> queueRef,
> >          long timeout
> >      
> >      ) throws IllegalArgumentException, InterruptedException,
> > 
> > CollectedException {
> > 
> >          if (timeout < 0) {
> >          
> >              throw new IllegalArgumentException("Negative timeout value");
> >          
> >          }
> >          // obtain lock
> >          Object lock = apply(queueRef, queue -> queue.lock);
> >          
> >          synchronized (lock) {
> >          
> >              Reference<? extends T> r = apply(queueRef,
> > 
> > ReferenceQueue<T>::reallyPoll);
> > 
> >              if (r != null) return r;
> >              long start = (timeout == 0) ? 0 : System.nanoTime();
> >              for (; ; ) {
> >              
> >                  lock.wait(timeout);
> >                  r = apply(queueRef, ReferenceQueue<T>::reallyPoll);
> >                  if (r != null) return r;
> >                  if (timeout != 0) {
> >                  
> >                      long end = System.nanoTime();
> >                      timeout -= (end - start) / 1000_000;
> >                      if (timeout <= 0) return null;
> >                      start = end;
> >                  
> >                  }
> >              
> >              }
> >          
> >          }
> >      
> >      }
> >      
> >      private static <R, T> R apply(
> >      
> >          Reference<? extends ReferenceQueue<T>> queueRef,
> >          Function<ReferenceQueue<T>, R> func
> >      
> >      ) throws CollectedException {
> >      
> >          ReferenceQueue<T> queue = queueRef.get();
> >          if (queue == null) throw new CollectedException();
> >          return func.apply(queue);
> >      
> >      }
> >      
> >      ...
> > 
> > This is basically a re-implementation of ReferenceQueue.remove(int
> > timeout) instance method in terms of a static method, which only briefly
> > "touches" the queue instance wrapped in a Reference but majority of time
> > it waits for notification while only holding a Reference to the queue.

... because as we look at the patch attached to issue:
https://bugs.openjdk.java.net/browse/JDK-8051843
we can find out it uses very similar approach:
https://bugs.openjdk.java.net/secure/attachment/21429/X.diff

 > But that's only half of the story. The other half is how a thread finds
> > out that the queue has been collected so it can exit. 

Well, the patch 
https://bugs.openjdk.java.net/secure/attachment/21429/X.diff
solves that by its

+    private static class Locks extends ReferenceQueue<Object> {
+        @Override
+        boolean enqueue(Reference<? extends Object> r) {
+            if (r instanceof Lock) {
+                synchronized (r) {
+                    r.notifyAll();
+                }
+            }
+            return false;
+        }
+    }

part. As the test at the end of the patch shows, the solution behaves find.
-jt





More information about the core-libs-dev mailing list