FULL PRODUCT VERSION :
java version "1.5.0_05"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_05-b05)
Java HotSpot(TM) Client VM (build 1.5.0_05-b05, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Error has been duplicated on both "Microsoft Windows XP [Version 5.1.2600]", and "Linux 2.4.21-15.ELsmp #1 SMP Mon May 24 12:29:17 EDT 2004 i686 i686 i386 GNU/Linux"
EXTRA RELEVANT SYSTEM CONFIGURATION :
Tried with and without the JIT, on Windows and Linux, with java 1.5.X (several versions on each of Windows and Linux)
A DESCRIPTION OF THE PROBLEM :
When a DelayQueue has objects with delays in the distant future, repeated calls to poll(long, TimeUnit) will eventually cause a heap overflow. replacing poll(long, TimeUnit) with take() makes the problem go away... but an empty loop on poll(long.TimeUnit) should be symantically equivalent to take(), so there is clearly a bug in poll().
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the source code included below... it is a single standalone file.
Run the source code.
The source code creates three objects on the queue, with timeouts that are 0, 3, and Integer.MAX_VALUE seconds into the future. You will see output when the first two items expire, then the code should sleep while waiting for the third. But on every machine we have tried, we see a heap overflow after some amount of time (usually between 5 and 30 seconds after the second item expires). The heap appears to overflow faster on faster machines, but that could just be a figment of our imaginations :-).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The expected output would be:
Wed May 24 10:51:00 EDT 2006: Woof!
Wed May 24 10:51:03 EDT 2006: Woof!
[Some date in the far distant future]: Woof!
...with the first Woof! happening immediately, the second Woof! happening approximately 3 seconds lately, and the final Woof! happening a very, very, very long time in the future.
ACTUAL -
The output is always:
Wed May 24 10:51:00 EDT 2006: Woof!
Wed May 24 10:51:03 EDT 2006: Woof!
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
...the time delay between the second Woof! and the out of memory is variable from machine to machine, but it always happens.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
(see the actual result information above)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.util.Date;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class Test {
private DelayQueue<Watchdog> queue = null;
public void doIt() {
queue = new DelayQueue<Watchdog>();
// Create three watchdogs, which will expire at
// various times between now, 3 seconds from now,
// and in a really, really long time from now...
queue.add(new Watchdog(0));
queue.add(new Watchdog(3));
queue.add(new Watchdog(Integer.MAX_VALUE));
for (;;) {
try {
//Watchdog fido = queue.take(); // THIS WORKS!
Watchdog fido = queue.poll(1, TimeUnit.SECONDS); // THIS BLOWS THE HEAP!
if (null != fido) {
fido.bark();
}
} catch (InterruptedException ie) {
// Ignore... InterruptedExceptions are fine.
}
}
}
public static void main(String args[]) {
Test test = new Test();
test.doIt();
}
private class Watchdog implements Delayed {
private long triggerTimeMillis = 0L;
public Watchdog(int delaySeconds) {
triggerTimeMillis = System.currentTimeMillis() + (1000L * delaySeconds);
}
public void bark() {
System.err.println(new Date() + ": Woof!");
}
public long getDelay(TimeUnit unit) {
long returnValue = unit.convert(triggerTimeMillis - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
return returnValue;
}
public int compareTo(Delayed other) {
long diff = getDelay(TimeUnit.MILLISECONDS) - other.getDelay(TimeUnit.MILLISECONDS);
return Long.signum(diff);
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
In my specific case, the poll(long, TimeUnit) could be replaced by take(), because the poll() is in an empty loop, and take() doesn't 'show the same behavior.
But poll(long, TimeUnit) strikes me as quite useless if it will blow up when there is a long wait until an item becomes available.