Revert^2 "Add reference processor tests""

This reverts commit bcf7ef1253691af61dab4062a358a2b903704d45.

Reason for revert: Land corrected version

PS1 is identical to aosp/1952438 .
PS3:
    Moves timeUnreachable into a nested function to avoid accidentally
    retained references.
    Disables "debuggable" runs of 2042-reference-processing, which
    sometimes take too long, since "debuggable" currrently disables use
    of AOT code. Further, test runs often combine it with "poison" which
    currently disables the fast interpreter.

Bug: 190867430
Bug: 189738006
Bug: 211784084

Test: Passes on host, Treehugger

Change-Id: I21d29158d0bc4c738fbf4c3f7c7ca881ae8b874b
diff --git a/TEST_MAPPING b/TEST_MAPPING
index afdced2..91a19d2 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -401,6 +401,12 @@
       "name": "art-run-test-2042-checker-dce-always-throw[com.google.android.art.apex]"
     },
     {
+      "name": "art-run-test-2042-reference-processing[com.google.android.art.apex]"
+    },
+    {
+      "name": "art-run-test-2043-reference-pauses[com.google.android.art.apex]"
+    },
+    {
       "name": "art-run-test-2231-checker-heap-poisoning[com.google.android.art.apex]"
     },
     {
@@ -1699,6 +1705,12 @@
       "name": "art-run-test-2042-checker-dce-always-throw"
     },
     {
+      "name": "art-run-test-2042-reference-processing"
+    },
+    {
+      "name": "art-run-test-2043-reference-pauses"
+    },
+    {
       "name": "art-run-test-2231-checker-heap-poisoning"
     },
     {
diff --git a/test/2042-reference-processing/Android.bp b/test/2042-reference-processing/Android.bp
new file mode 100644
index 0000000..a73b8d0
--- /dev/null
+++ b/test/2042-reference-processing/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2042-reference-processing`.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "art_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+    name: "art-run-test-2042-reference-processing",
+    defaults: ["art-run-test-defaults"],
+    test_config_template: ":art-run-test-target-template",
+    srcs: ["src/**/*.java"],
+    data: [
+        ":art-run-test-2042-reference-processing-expected-stdout",
+        ":art-run-test-2042-reference-processing-expected-stderr",
+    ],
+}
+
+// Test's expected standard output.
+genrule {
+    name: "art-run-test-2042-reference-processing-expected-stdout",
+    out: ["art-run-test-2042-reference-processing-expected-stdout.txt"],
+    srcs: ["expected-stdout.txt"],
+    cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+    name: "art-run-test-2042-reference-processing-expected-stderr",
+    out: ["art-run-test-2042-reference-processing-expected-stderr.txt"],
+    srcs: ["expected-stderr.txt"],
+    cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/2042-reference-processing/expected-stderr.txt b/test/2042-reference-processing/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2042-reference-processing/expected-stderr.txt
diff --git a/test/2042-reference-processing/expected-stdout.txt b/test/2042-reference-processing/expected-stdout.txt
new file mode 100644
index 0000000..8b3e832
--- /dev/null
+++ b/test/2042-reference-processing/expected-stdout.txt
@@ -0,0 +1,2 @@
+Starting
+Finished
diff --git a/test/2042-reference-processing/info.txt b/test/2042-reference-processing/info.txt
new file mode 100644
index 0000000..e2d7a99
--- /dev/null
+++ b/test/2042-reference-processing/info.txt
@@ -0,0 +1,6 @@
+A test for reference processing correctness.
+
+The emphasis here is on fundamental properties. In particular, references to
+unreachable referents should be enqueued, and this should ensure that uncleared
+References don't point to objects for which References were enqueued. We also
+check various other ordering properties for java.lang.ref.References.
diff --git a/test/2042-reference-processing/src/Main.java b/test/2042-reference-processing/src/Main.java
new file mode 100644
index 0000000..ed67052
--- /dev/null
+++ b/test/2042-reference-processing/src/Main.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.math.BigInteger;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.HashMap;
+import java.util.TreeMap;
+
+/**
+ * Test that objects get finalized and their references cleared in the right order.
+ *
+ * We maintain a list of nominally MAX_LIVE_OBJS numbered finalizable objects.
+ * We then alternately drop the last 50, and add 50 more. When we see an object finalized
+ * or its reference cleared, we make sure that the preceding objects in its group of 50
+ * have also had their references cleared. We also perform a number of other more
+ * straightforward checks, such as ensuring that all references are eventually cleared,
+ * and all objects are finalized.
+ */
+public class Main {
+    // TODO(b/216481630) Enable CHECK_PHANTOM_REFS. This currently occasionally reports a few
+    // PhantomReferences as not enqueued. If this report is correct, this needs to be tracked
+    // down and fixed.
+    static final boolean CHECK_PHANTOM_REFS = false;
+
+    static final int MAX_LIVE_OBJS = 150;
+    static final int DROP_OBJS = 50;  // Number of linked objects dropped in each batch.
+    static final int MIN_LIVE_OBJS = MAX_LIVE_OBJS - DROP_OBJS;
+    static final int TOTAL_OBJS = 200_000;  // Allocate this many finalizable objects in total.
+    static final boolean REPORT_DROPS = false;
+    static volatile boolean pleaseStop;
+
+    AtomicInteger totalFinalized = new AtomicInteger(0);
+    Object phantomRefsLock = new Object();
+    int maxDropped = 0;
+    int liveObjects = 0;
+
+    // Number of next finalizable object to be allocated.
+    int nextAllocated = 0;
+
+    // List of finalizable objects in descending order. We add to the front and drop
+    // from the rear.
+    FinalizableObject listHead;
+
+    // A possibly incomplete list of FinalizableObject indices that were finalized, but
+    // have yet to be checked for consistency with reference processing.
+    ArrayBlockingQueue<Integer> finalized = new ArrayBlockingQueue<>(20_000);
+
+    // Maps from object number to Reference; Cleared references are deleted when queues are
+    // processed.
+    TreeMap<Integer, MyWeakReference> weakRefs = new TreeMap<>();
+    HashMap<Integer, MyPhantomReference> phantomRefs = new HashMap<>();
+
+    class FinalizableObject {
+        int n;
+        FinalizableObject next;
+        FinalizableObject(int num, FinalizableObject nextObj) {
+            n = num;
+            next = nextObj;
+        }
+        protected void finalize() {
+            if (!inPhantomRefs(n)) {
+                System.out.println("PhantomRef enqueued before finalizer ran");
+            }
+            totalFinalized.incrementAndGet();
+            if (!finalized.offer(n) && REPORT_DROPS) {
+                System.out.println("Dropped finalization of " + n);
+            }
+        }
+    }
+    ReferenceQueue<FinalizableObject> refQueue = new ReferenceQueue<>();
+    class MyWeakReference extends WeakReference<FinalizableObject> {
+        int n;
+        MyWeakReference(FinalizableObject obj) {
+            super(obj, refQueue);
+            n = obj.n;
+        }
+    };
+    class MyPhantomReference extends PhantomReference<FinalizableObject> {
+        int n;
+        MyPhantomReference(FinalizableObject obj) {
+            super(obj, refQueue);
+            n = obj.n;
+        }
+    }
+    boolean inPhantomRefs(int n) {
+        synchronized(phantomRefsLock) {
+            MyPhantomReference ref = phantomRefs.get(n);
+            if (ref == null) {
+                return false;
+            }
+            if (ref.n != n) {
+                System.out.println("phantomRef retrieval failed");
+            }
+            return true;
+        }
+    }
+
+    void CheckOKToClearWeak(int num) {
+        if (num > maxDropped) {
+            System.out.println("WeakRef to live object " + num + " was cleared/enqueued.");
+        }
+        int batchEnd = (num / DROP_OBJS + 1) * DROP_OBJS;
+        for (MyWeakReference wr : weakRefs.subMap(num + 1, batchEnd).values()) {
+            if (wr.n <= num || wr.n / DROP_OBJS != num / DROP_OBJS) {
+                throw new AssertionError("MyWeakReference logic error!");
+            }
+            // wr referent was dropped in same batch and precedes it in list.
+            if (wr.get() != null) {
+                // This violates the WeakReference spec, and can result in strong references
+                // to objects that have been cleaned.
+                System.out.println("WeakReference to " + wr.n
+                    + " was erroneously cleared after " + num);
+            }
+        }
+    }
+
+    void CheckOKToClearPhantom(int num) {
+        if (num > maxDropped) {
+            System.out.println("PhantomRef to live object " + num + " was enqueued.");
+        }
+        MyWeakReference wr = weakRefs.get(num);
+        if (wr != null && wr.get() != null) {
+            System.out.println("PhantomRef cleared before WeakRef for " + num);
+        }
+    }
+
+    void emptyAndCheckQueues() {
+        // Check recently finalized objects for consistency with cleared references.
+        while (true) {
+            Integer num = finalized.poll();
+            if (num == null) {
+                break;
+            }
+            MyWeakReference wr = weakRefs.get(num);
+            if (wr != null) {
+                if (wr.n != num) {
+                    System.out.println("Finalization logic error!");
+                }
+                if (wr.get() != null) {
+                    System.out.println("Finalizing object with uncleared reference");
+                }
+            }
+            CheckOKToClearWeak(num);
+        }
+        // Check recently enqueued references for consistency.
+        while (true) {
+            Reference<FinalizableObject> ref = (Reference<FinalizableObject>) refQueue.poll();
+            if (ref == null) {
+                break;
+            }
+            if (ref instanceof MyWeakReference) {
+                MyWeakReference wr = (MyWeakReference) ref;
+                if (wr.get() != null) {
+                    System.out.println("WeakRef " + wr.n + " enqueued but not cleared");
+                }
+                CheckOKToClearWeak(wr.n);
+                if (weakRefs.remove(Integer.valueOf(wr.n)) != ref) {
+                    System.out.println("Missing WeakReference: " + wr.n);
+                }
+            } else if (ref instanceof MyPhantomReference) {
+                MyPhantomReference pr = (MyPhantomReference) ref;
+                CheckOKToClearPhantom(pr.n);
+                if (phantomRefs.remove(Integer.valueOf(pr.n)) != ref) {
+                    System.out.println("Missing PhantomReference: " + pr.n);
+                }
+            } else {
+                System.out.println("Found unrecognized reference in queue");
+            }
+        }
+    }
+
+
+    /**
+     * Add n objects to the head of the list. These will be assigned the next n consecutive
+     * numbers after the current head of the list.
+     */
+    void addObjects(int n) {
+        for (int i = 0; i < n; ++i) {
+            int me = nextAllocated++;
+            listHead = new FinalizableObject(me, listHead);
+            weakRefs.put(me, new MyWeakReference(listHead));
+            synchronized(phantomRefsLock) {
+                phantomRefs.put(me, new MyPhantomReference(listHead));
+            }
+        }
+        liveObjects += n;
+    }
+
+    /**
+     * Drop n finalizable objects from the tail of the list. These are the lowest-numbered objects
+     * in the list.
+     */
+    void dropObjects(int n) {
+        FinalizableObject list = listHead;
+        FinalizableObject last = null;
+        if (n > liveObjects) {
+            System.out.println("Removing too many elements");
+        }
+        if (liveObjects == n) {
+            maxDropped = list.n;
+            listHead = null;
+        } else {
+            final int skip = liveObjects - n;
+            for (int i = 0; i < skip; ++i) {
+                last = list;
+                list = list.next;
+            }
+            int expected = nextAllocated - skip - 1;
+            if (list.n != expected) {
+                System.out.println("dropObjects found " + list.n + " but expected " + expected);
+            }
+            maxDropped = expected;
+            last.next = null;
+        }
+        liveObjects -= n;
+    }
+
+    void testLoop() {
+        System.out.println("Starting");
+        addObjects(MIN_LIVE_OBJS);
+        final int ITERS = (TOTAL_OBJS - MIN_LIVE_OBJS) / DROP_OBJS;
+        for (int i = 0; i < ITERS; ++i) {
+            addObjects(DROP_OBJS);
+            if (liveObjects != MAX_LIVE_OBJS) {
+                System.out.println("Unexpected live object count");
+            }
+            dropObjects(DROP_OBJS);
+            emptyAndCheckQueues();
+        }
+        dropObjects(MIN_LIVE_OBJS);
+        if (liveObjects != 0 || listHead != null) {
+            System.out.println("Unexpected live objecs at end");
+        }
+        if (maxDropped != TOTAL_OBJS - 1) {
+            System.out.println("Unexpected dropped object count: " + maxDropped);
+        }
+        for (int i = 0; i < 2; ++i) {
+            Runtime.getRuntime().gc();
+            System.runFinalization();
+            emptyAndCheckQueues();
+        }
+        if (!weakRefs.isEmpty()) {
+            System.out.println("Weak Reference map nonempty size = " + weakRefs.size());
+        }
+        if (CHECK_PHANTOM_REFS && !phantomRefs.isEmpty()) {
+            try {
+                Thread.sleep(500);
+            } catch (InterruptedException e) {
+                System.out.println("Unexpected interrupt");
+            }
+            if (!phantomRefs.isEmpty()) {
+                System.out.println("Phantom Reference map nonempty size = " + phantomRefs.size());
+                System.out.print("First elements:");
+                int i = 0;
+                for (MyPhantomReference pr : phantomRefs.values()) {
+                    System.out.print(" " + pr.n);
+                    if (++i > 10) {
+                        break;
+                    }
+                }
+                System.out.println("");
+            }
+        }
+        if (totalFinalized.get() != TOTAL_OBJS) {
+            System.out.println("Finalized only " + totalFinalized + " objects");
+        }
+    }
+
+    static Runnable causeGCs = new Runnable() {
+        public void run() {
+            // Allocate a lot.
+            BigInteger counter = BigInteger.ZERO;
+            while (!pleaseStop) {
+                counter = counter.add(BigInteger.TEN);
+            }
+            // Look at counter to reduce chance of optimizing out the allocation.
+            if (counter.longValue() % 10 != 0) {
+                 System.out.println("Bad causeGCs counter value: " + counter);
+            }
+        }
+    };
+
+    public static void main(String[] args) throws Exception {
+        Main theTest = new Main();
+        Thread gcThread = new Thread(causeGCs);
+        gcThread.setDaemon(true);  // Terminate if main thread dies.
+        gcThread.start();
+        theTest.testLoop();
+        pleaseStop = true;
+        gcThread.join();
+        System.out.println("Finished");
+    }
+}
diff --git a/test/2043-reference-pauses/Android.bp b/test/2043-reference-pauses/Android.bp
new file mode 100644
index 0000000..a84aea2
--- /dev/null
+++ b/test/2043-reference-pauses/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2043-reference-pauses`.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "art_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+    name: "art-run-test-2043-reference-pauses",
+    defaults: ["art-run-test-defaults"],
+    test_config_template: ":art-run-test-target-template",
+    srcs: ["src/**/*.java"],
+    data: [
+        ":art-run-test-2043-reference-pauses-expected-stdout",
+        ":art-run-test-2043-reference-pauses-expected-stderr",
+    ],
+}
+
+// Test's expected standard output.
+genrule {
+    name: "art-run-test-2043-reference-pauses-expected-stdout",
+    out: ["art-run-test-2043-reference-pauses-expected-stdout.txt"],
+    srcs: ["expected-stdout.txt"],
+    cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+    name: "art-run-test-2043-reference-pauses-expected-stderr",
+    out: ["art-run-test-2043-reference-pauses-expected-stderr.txt"],
+    srcs: ["expected-stderr.txt"],
+    cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/2043-reference-pauses/expected-stderr.txt b/test/2043-reference-pauses/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2043-reference-pauses/expected-stderr.txt
diff --git a/test/2043-reference-pauses/expected-stdout.txt b/test/2043-reference-pauses/expected-stdout.txt
new file mode 100644
index 0000000..8b3e832
--- /dev/null
+++ b/test/2043-reference-pauses/expected-stdout.txt
@@ -0,0 +1,2 @@
+Starting
+Finished
diff --git a/test/2043-reference-pauses/info.txt b/test/2043-reference-pauses/info.txt
new file mode 100644
index 0000000..f76fa32
--- /dev/null
+++ b/test/2043-reference-pauses/info.txt
@@ -0,0 +1,5 @@
+Tests WeakReference processing and retention of objects needed by finalizers.
+
+Can be used as Reference.get() blocking benchmark by setting PRINT_TIMES to
+true. This will print maximum observed latencies for Reference.get() when
+significant memory is only reachable from SoftReferences and Finalizers.
diff --git a/test/2043-reference-pauses/src/Main.java b/test/2043-reference-pauses/src/Main.java
new file mode 100644
index 0000000..dc64d9a
--- /dev/null
+++ b/test/2043-reference-pauses/src/Main.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.lang.ref.SoftReference;
+import java.math.BigInteger;
+import java.util.ArrayList;
+
+/**
+ * Basic test of WeakReferences with large amounts of memory that's only reachable through
+ * finalizers. Also makes sure that finalizer-reachable data is not collected.
+ * Can easily be modified to time Reference.get() blocking.
+ */
+public class Main {
+    static final boolean PRINT_TIMES = false;  // true will cause benchmark failure.
+    // Data structures repeatedly allocated in background to trigger GC.
+    // Size of finalizer reachable trees.
+    static final int TREE_HEIGHT = 15;  // Trees contain 2^TREE_HEIGHT -1 allocated objects.
+    // Number of finalizable tree-owning objects that exist at one point.
+    static final int N_RESURRECTING_OBJECTS = 10;
+    // Number of short-lived, not finalizer-reachable, objects allocated between trees.
+    static final int N_PLAIN_OBJECTS = 20_000;
+    // Number of SoftReferences to CBTs we allocate.
+    static final int N_SOFTREFS = 10;
+
+    static final boolean BACKGROUND_GC_THREAD = true;
+    static final int NBATCHES = 10;
+    static final int NREFS = PRINT_TIMES ? 1_000_000 : 300_000;  // Multiple of NBATCHES.
+    static final int REFS_PER_BATCH = NREFS / NBATCHES;
+
+    static volatile boolean pleaseStop = false;
+
+    // Large array of WeakReferences filled and accessed by tests below.
+    ArrayList<WeakReference<Integer>> weakRefs = new ArrayList<>(NREFS);
+
+    /**
+     * Complete binary tree data structure. make(n) takes O(2^n) space.
+     */
+    static class CBT {
+        CBT left;
+        CBT right;
+        CBT(CBT l, CBT r) {
+            left = l;
+            right = r;
+        }
+        static CBT make(int n) {
+            if (n == 0) {
+                return null;
+            }
+            return new CBT(make(n - 1), make(n - 1));
+        }
+        /**
+         * Check that path described by bit-vector path has the correct length.
+         */
+        void check(int n, int path) {
+            CBT current = this;
+            for (int i = 0; i < n; i++, path = path >>> 1) {
+                // Unexpectedly short paths result in NPE.
+                if ((path & 1) == 0) {
+                    current = current.left;
+                } else {
+                    current = current.right;
+                }
+            }
+            if (current != null) {
+                System.out.println("Complete binary tree path too long");
+            }
+        }
+    }
+
+
+    /**
+     * A finalizable object that refers to O(2^TREE_HEIGHT) otherwise unreachable memory.
+     * When finalized, it creates a new identical object, making sure that one always stays
+     * around.
+     */
+    static class ResurrectingObject {
+        CBT stuff;
+        ResurrectingObject() {
+            stuff = CBT.make(TREE_HEIGHT);
+        }
+        static ResurrectingObject a[] = new ResurrectingObject[2];
+        static int i = 0;
+        static synchronized void allocOne() {
+            a[(++i) % 2] = new ResurrectingObject();
+            // Check the previous one to make it hard to optimize anything out.
+            if (i > 1) {
+                a[(i + 1) % 2].stuff.check(TREE_HEIGHT, i /* weirdly interpreted as path */);
+            }
+        }
+        protected void finalize() {
+            stuff.check(TREE_HEIGHT, 42 /* Some path descriptor */);
+            // Allocate a new one to replace this one.
+            allocOne();
+        }
+    }
+
+    void fillWeakRefs() {
+        for (int i = 0; i < NREFS; ++i) {
+             weakRefs.add(null);
+        }
+    }
+
+    /*
+     * Return maximum observed time in nanos to dereference a WeakReference to an unreachable
+     * object. weakRefs is presumed to be pre-filled to have the correct size.
+     */
+    long timeUnreachableInner() {
+        long maxNanos = 0;
+        // Fill weakRefs with WeakReferences to unreachable integers, a batch at a time.
+        // Then time and test .get() calls on carefully sampled array entries, some of which
+        // will have been cleared.
+        for (int i = 0; i < NBATCHES; ++i) {
+            for (int j = 0; j < REFS_PER_BATCH; ++j) {
+                weakRefs.set(i * REFS_PER_BATCH + j,
+                        new WeakReference(new Integer(i * REFS_PER_BATCH + j)));
+            }
+            try {
+                Thread.sleep(50);
+            } catch (InterruptedException e) {
+                System.out.println("Unexpected exception");
+            }
+            // Iterate over the filled-in section of weakRefs, but look only at a subset of the
+            // elements, making sure the subsets for different top-level iterations are disjoint.
+            // Otherwise the get() calls here will extend the lifetimes of the referents, and we
+            // may never see any cleared WeakReferences.
+            for (int j = (i + 1) * REFS_PER_BATCH - i - 1; j >= 0; j -= NBATCHES) {
+                WeakReference<Integer> wr = weakRefs.get(j);
+                if (wr != null) {
+                    long startNanos = System.nanoTime();
+                    Integer referent = wr.get();
+                    long totalNanos = System.nanoTime() - startNanos;
+                    if (referent == null) {
+                        // Optimization to reduce max space use and scanning time.
+                        weakRefs.set(j, null);
+                    }
+                    maxNanos = Math.max(maxNanos, totalNanos);
+                    if (referent != null && referent.intValue() != j) {
+                        System.out.println("Unexpected referent; expected " + j + " got "
+                                + referent.intValue());
+                    }
+                }
+            }
+        }
+        return maxNanos;
+    }
+
+    /*
+     * Wrapper for the above that also checks that references were reclaimed.
+     * We do this separately to make sure any stack references from the core of the
+     * test are gone. Empirically, we otherwise sometimes see the zeroth WeakReference
+     * not reclaimed.
+     */
+    long timeUnreachable() {
+        long maxNanos = timeUnreachableInner();
+        Runtime.getRuntime().gc();
+        System.runFinalization();  // Presumed to wait for reference clearing.
+        for (int i = 0; i < NREFS; ++i) {
+            if (weakRefs.get(i) != null && weakRefs.get(i).get() != null) {
+                System.out.println("WeakReference to " + i + " wasn't cleared");
+            }
+        }
+        return maxNanos;
+    }
+
+    /**
+     * Return maximum observed time in nanos to dereference a WeakReference to a reachable
+     * object. Overwrites weakRefs, which is presumed to have NREFS entries already.
+    */
+    long timeReachable() {
+        long maxNanos = 0;
+        // Similar to the above, but we use WeakReferences to otherwise reachable objects,
+        // which should thus not get cleared.
+        Integer[] strongRefs = new Integer[NREFS];
+        for (int i = 0; i < NBATCHES; ++i) {
+            for (int j = i * REFS_PER_BATCH; j < (i + 1) * NREFS / NBATCHES; ++j) {
+                Integer newObj = new Integer(j);
+                strongRefs[j] = newObj;
+                weakRefs.set(j, new WeakReference(newObj));
+            }
+            for (int j = (i + 1) * REFS_PER_BATCH - 1; j >= 0; --j) {
+                WeakReference<Integer> wr = weakRefs.get(j);
+                long startNanos = System.nanoTime();
+                Integer referent = wr.get();
+                long totalNanos = System.nanoTime() - startNanos;
+                maxNanos = Math.max(maxNanos, totalNanos);
+                if (referent == null) {
+                    System.out.println("Unexpectedly cleared referent at " + j);
+                } else if (referent.intValue() != j) {
+                    System.out.println("Unexpected reachable referent; expected " + j + " got "
+                            + referent.intValue());
+                }
+            }
+        }
+        Reference.reachabilityFence(strongRefs);
+        return maxNanos;
+    }
+
+    void runTest() {
+        System.out.println("Starting");
+        fillWeakRefs();
+        long unreachableNanos = timeUnreachable();
+        if (PRINT_TIMES) {
+            System.out.println("Finished timeUnrechable()");
+        }
+        long reachableNanos = timeReachable();
+        String unreachableMillis =
+                String. format("%,.3f", ((double) unreachableNanos) / 1_000_000);
+        String reachableMillis =
+                String. format("%,.3f", ((double) reachableNanos) / 1_000_000);
+        if (PRINT_TIMES) {
+            System.out.println(
+                    "Max time for WeakReference.get (unreachable): " + unreachableMillis);
+            System.out.println(
+                    "Max time for WeakReference.get (reachable): " + reachableMillis);
+        }
+        // Only report extremely egregious pauses to avoid spurious failures.
+        if (unreachableNanos > 10_000_000_000L) {
+            System.out.println("WeakReference.get (unreachable) time unreasonably long");
+        }
+        if (reachableNanos > 10_000_000_000L) {
+            System.out.println("WeakReference.get (reachable) time unreasonably long");
+        }
+    }
+
+    /**
+     * Allocate and GC a lot, while keeping significant amounts of finalizer and
+     * SoftReference-reachable memory around.
+     */
+    static Runnable allocFinalizable = new Runnable() {
+        public void run() {
+            // Allocate and drop some finalizable objects that take a long time
+            // to mark. Designed to be hard to optimize away. Each of these objects will
+            // build a new one in its finalizer before really going away.
+            ArrayList<SoftReference<CBT>> softRefs = new ArrayList<>(N_SOFTREFS);
+            for (int i = 0; i < N_SOFTREFS; ++i) {
+                // These should not normally get reclaimed, since we shouldn't run out of
+                // memory. They do increase tracing time.
+                softRefs.add(new SoftReference(CBT.make(TREE_HEIGHT)));
+            }
+            for (int i = 0; i < N_RESURRECTING_OBJECTS; ++i) {
+                ResurrectingObject.allocOne();
+            }
+            BigInteger counter = BigInteger.ZERO;
+            for (int i = 1; !pleaseStop; ++i) {
+                // Allocate a lot of short-lived objects, using BigIntegers to minimize the chance
+                // of the allocation getting optimized out. This makes things slightly more
+                // realistic, since not all objects will be finalizer reachable.
+                for (int j = 0; j < N_PLAIN_OBJECTS / 2; ++j) {
+                    counter = counter.add(BigInteger.TEN);
+                }
+                // Look at counter to reduce chance of optimizing out the allocation.
+                if (counter.longValue() % 10 != 0) {
+                    System.out.println("Bad allocFinalizable counter value: " + counter);
+                }
+                // Explicitly collect here, mostly to prevent heap growth. Otherwise we get
+                // ahead of the GC and eventually block on it.
+                Runtime.getRuntime().gc();
+                if (PRINT_TIMES && i % 100 == 0) {
+                    System.out.println("Collected " + i + " times");
+                }
+            }
+            // To be safe, access softRefs.
+            final CBT sample = softRefs.get(N_SOFTREFS / 2).get();
+            if (sample != null) {
+              sample.check(TREE_HEIGHT, 47 /* some path descriptor */);
+            }
+        }
+    };
+
+    public static void main(String[] args) throws Exception {
+        Main theTest = new Main();
+        Thread allocThread = null;
+        if (BACKGROUND_GC_THREAD) {
+            allocThread = new Thread(allocFinalizable);
+            allocThread.setDaemon(true);  // Terminate if main thread dies.
+            allocThread.start();
+        }
+        theTest.runTest();
+        if (BACKGROUND_GC_THREAD) {
+            pleaseStop = true;
+            allocThread.join();
+        }
+        System.out.println("Finished");
+    }
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 3e485f6..083ff80 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1358,8 +1358,13 @@
         "description": "Interpreting BigInteger.add() is too slow (timeouts)"
     },
     {
-        "tests": ["2029-contended-monitors"],
+        "tests": ["2029-contended-monitors", "2043-reference-pauses"],
         "variant": "interpreter | interp-ac | gcstress | trace",
+        "description": ["Slow tests. Prone to timeouts."]
+    },
+    {
+        "tests": ["2042-reference-processing"],
+        "variant": "interpreter | interp-ac | gcstress | trace | debuggable",
         "description": ["Slow test. Prone to timeouts."]
     },
     {