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