JDK-6853696 : (ref) ReferenceQueue.remove(timeout) may return null even if timeout has not expired
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 6u14
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2009-06-22
  • Updated: 2015-01-21
  • Resolved: 2014-02-27
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u40Fixed 9 b04Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_10"
Java(TM) SE Runtime Environment (build 1.6.0_10-b33)
Java HotSpot(TM) Client VM (build 11.0-b15, mixed mode, sharing)

java version "1.7.0-ea-fastdebug"
Java(TM) SE Runtime Environment (build 1.7.0-ea-fastdebug-b58)
Java HotSpot(TM) Client VM (build 16.0-b02-fastdebug, mixed mode)


A DESCRIPTION OF THE PROBLEM :
ReferenceQueue.remove(timeout) may return null even if given timeout period has not expired.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
ReferenceQueue.remove(timeout) should wait until either next reference object in this queue becomes available or the given timeout period expires, but not earlier or far later.
ACTUAL -
Exception in thread "main" java.lang.Error: timeout: 5000; actual time: 1000
	at Main.main(Main.java:38)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;


public class Main extends Thread {
	static ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
	static Object referent = new Object();
	static WeakReference<Object> reference = new WeakReference<Object>(referent, queue);

	long timeout;
	long actual;
	boolean suspect;

	Main(long timeout) {
		this.timeout = timeout;
	}

	public static void main(String... args) throws Exception {
		final int LONG_TIMEOUT = 5000;
		final int SHORT_WAIT_TIME = 1000;
		final int TOLERANCE = 1000;
		assert LONG_TIMEOUT > SHORT_WAIT_TIME : "Bad initial parameters";
		assert LONG_TIMEOUT - SHORT_WAIT_TIME > TOLERANCE : "Bad initial parameters";
		Main[] threads = new Main[2];
		for (int i = 0, n = threads.length; i < n; ++i) {
			threads[i] = new Main(LONG_TIMEOUT);
			threads[i].start();
		}
		Thread.sleep(SHORT_WAIT_TIME);
		referent = null;
		System.gc();
		for (Main thread : threads) {
			thread.join();
		}
		for (Main thread : threads) {
			if (thread.suspect && Math.abs(thread.timeout - thread.actual) > TOLERANCE) {
				throw new Error("timeout: " + thread.timeout + "; actual time: " + thread.actual);
			}
		}
	}

	public void run() {
		try {
			long start = System.currentTimeMillis();
			Reference<?> reference = queue.remove(timeout);
			actual = System.currentTimeMillis() - start;
			suspect = reference == null;
		} catch (InterruptedException exc) {
			exc.printStackTrace();
		}
	}
}

---------- END SOURCE ----------