--- old/src/java.base/share/classes/java/lang/ref/FinalReference.java 2017-02-24 17:13:32.000000000 -0800 +++ new/src/java.base/share/classes/java/lang/ref/FinalReference.java 2017-02-24 17:13:32.000000000 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,4 +33,9 @@ public FinalReference(T referent, ReferenceQueue q) { super(referent, q); } + + @Override + public boolean enqueue() { + throw new UnsupportedOperationException("should never reach here"); + } } --- old/src/java.base/share/classes/java/lang/ref/Reference.java 2017-02-24 17:13:33.000000000 -0800 +++ new/src/java.base/share/classes/java/lang/ref/Reference.java 2017-02-24 17:13:33.000000000 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -140,15 +140,24 @@ } } - /* Atomically get and clear (set to null) the VM's pending list. + /* + * system property to disable clearing before enqueuing. + */ + private static final boolean disableClearBeforeEnqueue + = Boolean.getBoolean("jdk.ref.disableClearBeforeEnqueue"); + + /* + * Atomically get and clear (set to null) the VM's pending list. */ private static native Reference getAndClearReferencePendingList(); - /* Test whether the VM's pending list contains any entries. + /* + * Test whether the VM's pending list contains any entries. */ private static native boolean hasReferencePendingList(); - /* Wait until the VM's pending list may be non-null. + /* + * Wait until the VM's pending list may be non-null. */ private static native void waitForReferencePendingList(); @@ -261,7 +270,6 @@ this.referent = null; } - /* -- Queue operations -- */ /** @@ -278,8 +286,8 @@ } /** - * Adds this reference object to the queue with which it is registered, - * if any. + * Clears this reference object and adds it to the queue with which + * it is registered, if any. * *

This method is invoked only by Java code; when the garbage collector * enqueues references it does so directly, without invoking this method. @@ -289,10 +297,11 @@ * it was not registered with a queue when it was created */ public boolean enqueue() { + if (!disableClearBeforeEnqueue) + this.referent = null; return this.queue.enqueue(this); } - /* -- Constructors -- */ Reference(T referent) { @@ -419,5 +428,4 @@ // HotSpot needs to retain the ref and not GC it before a call to this // method } - } --- old/test/java/lang/ref/ReferenceEnqueue.java 2017-02-24 17:13:34.000000000 -0800 +++ new/test/java/lang/ref/ReferenceEnqueue.java 2017-02-24 17:13:34.000000000 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,17 +22,24 @@ */ /* @test - * @bug 4268317 8132306 + * @bug 4268317 8132306 8175797 * @summary Test if Reference.enqueue() works properly with GC + * @run main ReferenceEnqueue + * @run main/othervm -Djdk.ref.disableClearAndEnqueue=true ReferenceEnqueue + * @run main/othervm -Djava.security.manager ReferenceEnqueue */ import java.lang.ref.*; +import java.util.ArrayList; +import java.util.List; public class ReferenceEnqueue { public static void main(String args[]) throws Exception { - for (int i=0; i < 5; i++) + for (int i=0; i < 5; i++) { new WeakRef().run(); + new ExplicitEnqueue().run(); + } System.out.println("Test passed."); } @@ -76,4 +83,50 @@ } } } + + static class ExplicitEnqueue { + final ReferenceQueue queue = new ReferenceQueue<>(); + final List> refs = new ArrayList<>(); + final int iterations = 1000; + final boolean disableClearAndEnqueue = + Boolean.parseBoolean("jdk.ref.disableClearAndEnqueue"); + + ExplicitEnqueue() { + this.refs.add(new SoftReference<>(new Object(), queue)); + this.refs.add(new WeakReference<>(new Object(), queue)); + this.refs.add(new PhantomReference<>(new Object(), queue)); + } + + void run() throws InterruptedException { + for (Reference ref : refs) { + if (ref.enqueue() == false) { + throw new RuntimeException("Error: enqueue failed"); + } + if (disableClearAndEnqueue && ref.get() == null) { + throw new RuntimeException("Error: clearing should be disabled"); + } + if (!disableClearAndEnqueue && ref.get() != null) { + throw new RuntimeException("Error: referent must be cleared"); + } + } + + System.gc(); + for (int i = 0; refs.size() > 0 && i < iterations; i++) { + Reference ref = (Reference)queue.poll(); + if (ref == null) { + System.gc(); + Thread.sleep(100); + continue; + } + + if (refs.remove(ref) == false) { + throw new RuntimeException("Error: unknown reference " + ref); + } + } + + if (!refs.isEmpty()) { + throw new RuntimeException("Error: not all references are removed"); + } + } + } }