From a3c459c489dc2a9da110bf506c6a924ed2b9b353 Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier Date: Mon, 9 Oct 2023 11:25:45 +0200 Subject: [PATCH 1/4] Backport 2e332c276052554540da0998316a5a99bc350cd6 --- test/lib/jdk/test/lib/util/ForceGC.java | 27 +++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/test/lib/jdk/test/lib/util/ForceGC.java b/test/lib/jdk/test/lib/util/ForceGC.java index 888869dc53a..6db98220f58 100644 --- a/test/lib/jdk/test/lib/util/ForceGC.java +++ b/test/lib/jdk/test/lib/util/ForceGC.java @@ -32,21 +32,24 @@ * Utility class to invoke System.gc() */ public class ForceGC { - private final CountDownLatch cleanerInvoked = new CountDownLatch(1); - private final Cleaner cleaner = Cleaner.create(); + private final static Cleaner cleaner = Cleaner.create(); + + private final CountDownLatch cleanerInvoked; private Object o; + private int gcCount = 0; public ForceGC() { this.o = new Object(); - cleaner.register(o, () -> cleanerInvoked.countDown()); + this.cleanerInvoked = new CountDownLatch(1); + cleaner.register(o, cleanerInvoked::countDown); } private void doit(int iter) { try { for (int i = 0; i < 10; i++) { System.gc(); - System.out.println("doit() iter: " + iter + ", gc " + i); - if (cleanerInvoked.await(1L, TimeUnit.SECONDS)) { + gcCount++; + if (cleanerInvoked.await(100L, TimeUnit.MILLISECONDS)) { return; } } @@ -68,12 +71,20 @@ public boolean await(BooleanSupplier s) { o = null; // Keep reference to Object until now, to ensure the Cleaner // doesn't count down the latch before await() is called. for (int i = 0; i < 10; i++) { - if (s.getAsBoolean()) return true; - doit(i); - try { Thread.sleep(1000); } catch (InterruptedException e) { + if (s.getAsBoolean()) { + System.out.println("ForceGC condition met after System.gc() " + gcCount + " times"); + return true; + } + + doIt(i); + try { + Thread.sleep(100); + } catch (InterruptedException e) { throw new AssertionError("unexpected interrupted sleep", e); } } + + System.out.println("ForceGC condition not met after System.gc() " + gcCount + " times"); return false; } } From e6a560ce98ba59b4b7bfcec200c6ae0af531b7fc Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier Date: Mon, 9 Oct 2023 11:26:18 +0200 Subject: [PATCH 2/4] Backport a50b06e85124f61b5133189a2a2e461753d5d9e7 --- test/lib/jdk/test/lib/util/ForceGC.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lib/jdk/test/lib/util/ForceGC.java b/test/lib/jdk/test/lib/util/ForceGC.java index 6db98220f58..1a71dd229d1 100644 --- a/test/lib/jdk/test/lib/util/ForceGC.java +++ b/test/lib/jdk/test/lib/util/ForceGC.java @@ -76,7 +76,7 @@ public boolean await(BooleanSupplier s) { return true; } - doIt(i); + doit(i); try { Thread.sleep(100); } catch (InterruptedException e) { From 28957c832943a315c17780d8976a179b9b3342ce Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier Date: Mon, 9 Oct 2023 14:32:04 +0200 Subject: [PATCH 3/4] Backport 82a8bd7e92a1867b0c82f051361938be8610428d --- .../TestOSCClassLoaderLeak.java | 11 ++- .../nativeLibrary/NativeLibraryTest.java | 5 +- .../defineHiddenClass/UnloadingTest.java | 13 ++- .../ReflectionCallerCacheTest.java | 6 +- .../pkcs11/Provider/MultipleLogins.java | 4 +- test/lib/jdk/test/lib/util/ForceGC.java | 80 ++++++++----------- 6 files changed, 49 insertions(+), 70 deletions(-) diff --git a/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java b/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java index 3a99760bb6e..1816aa45fe7 100644 --- a/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java +++ b/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, 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 @@ -30,8 +30,8 @@ import java.io.Serializable; import java.util.Arrays; import org.testng.annotations.Test; -import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertFalse; import jdk.test.lib.util.ForceGC; @@ -54,15 +54,14 @@ public void testClassLoaderLeak() throws Exception { objectStreamClass_MemoryLeakExample.toString(); WeakReference myOwnClassLoaderWeakReference = new WeakReference<>(myOwnClassLoader); - assertNotNull(myOwnClassLoaderWeakReference.get()); + assertFalse(myOwnClassLoaderWeakReference.refersTo(null)); objectStreamClass_MemoryLeakExample = null; myOwnClassLoader = null; loadClass = null; con = null; - assertNotNull(myOwnClassLoaderWeakReference.get()); + assertFalse(myOwnClassLoaderWeakReference.refersTo(null)); - ForceGC gc = new ForceGC(); - assertTrue(gc.await(() -> myOwnClassLoaderWeakReference.get() == null)); + assertTrue(ForceGC.wait(() -> myOwnClassLoaderWeakReference.refersTo(null))); } } diff --git a/test/jdk/java/lang/ClassLoader/nativeLibrary/NativeLibraryTest.java b/test/jdk/java/lang/ClassLoader/nativeLibrary/NativeLibraryTest.java index ab7194713d5..46de24d1bcd 100644 --- a/test/jdk/java/lang/ClassLoader/nativeLibrary/NativeLibraryTest.java +++ b/test/jdk/java/lang/ClassLoader/nativeLibrary/NativeLibraryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2022, 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 @@ -66,9 +66,8 @@ public static void main(String... args) throws Exception { // Unload the class loader and native library, and give the Cleaner // thread a chance to unload the native library. // unloadedCount is incremented when the native library is unloaded. - ForceGC gc = new ForceGC(); final int finalCount = count; - if (!gc.await(() -> finalCount == unloadedCount)) { + if (!ForceGC.wait(() -> finalCount == unloadedCount)) { throw new RuntimeException("Expected unloaded=" + count + " but got=" + unloadedCount); } diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java b/test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java index c3cdb2b7095..2e5c4837282 100644 --- a/test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java +++ b/test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022, 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 @@ -214,18 +214,15 @@ private HiddenClassUnloader(Class hc) { } void unload() { - // Force garbage collection to trigger unloading of class loader and native library - ForceGC gc = new ForceGC(); - assertTrue(gc.await(() -> weakRef.get() == null)); - - if (weakRef.get() != null) { + // Force garbage collection to trigger unloading of class loader + // and native library. + if (!ForceGC.wait(() -> weakRef.refersTo(null))) { throw new RuntimeException("loader " + " not unloaded!"); } } boolean tryUnload() { - ForceGC gc = new ForceGC(); - return gc.await(() -> weakRef.get() == null); + return ForceGC.wait(() -> weakRef.refersTo(null)); } } diff --git a/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java b/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java index f245fb55f69..47dfa5a019d 100644 --- a/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java +++ b/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2022, 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 @@ -107,9 +107,7 @@ private void load(String classname) throws Exception { WeakReference weakLoader = loadAndRunClass(classname); // Force garbage collection to trigger unloading of class loader - new ForceGC().await(() -> weakLoader.get() == null); - - if (weakLoader.get() != null) { + if (!ForceGC.wait(() -> weakLoader.refersTo(null))) { throw new RuntimeException("Class " + classname + " not unloaded!"); } } diff --git a/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.java b/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.java index 0a507f0a633..5d6fb68aceb 100644 --- a/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.java +++ b/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.java @@ -89,10 +89,8 @@ public static void main(String[] args) throws Exception { Security.removeProvider(providers[i].getName()); providers[i] = null; - ForceGC gc = new ForceGC(); int finalI = i; - gc.await(() -> weakRef[finalI].get() == null); - if (!weakRef[i].refersTo(null)) { + if (!ForceGC.wait(() -> weakRef[finalI].refersTo(null))) { throw new RuntimeException("Expected SunPKCS11 Provider to be GC'ed.."); } } diff --git a/test/lib/jdk/test/lib/util/ForceGC.java b/test/lib/jdk/test/lib/util/ForceGC.java index 1a71dd229d1..3c9ebc4d35e 100644 --- a/test/lib/jdk/test/lib/util/ForceGC.java +++ b/test/lib/jdk/test/lib/util/ForceGC.java @@ -23,68 +23,56 @@ package jdk.test.lib.util; -import java.lang.ref.Cleaner; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; import java.util.function.BooleanSupplier; /** * Utility class to invoke System.gc() */ public class ForceGC { - private final static Cleaner cleaner = Cleaner.create(); - - private final CountDownLatch cleanerInvoked; - private Object o; - private int gcCount = 0; - - public ForceGC() { - this.o = new Object(); - this.cleanerInvoked = new CountDownLatch(1); - cleaner.register(o, cleanerInvoked::countDown); - } - - private void doit(int iter) { - try { - for (int i = 0; i < 10; i++) { - System.gc(); - gcCount++; - if (cleanerInvoked.await(100L, TimeUnit.MILLISECONDS)) { - return; - } - } - } catch (InterruptedException unexpected) { - throw new AssertionError("unexpected InterruptedException"); - } - } + // The jtreg testing timeout factor. + private static final double TIMEOUT_FACTOR = Double.valueOf( + System.getProperty("test.timeout.factor", "1.0")); /** - * Causes the current thread to wait until the {@code BooleanSupplier} returns true, - * unless the thread is interrupted or a predefined waiting time elapses. + * Causes the current thread to wait until the {@code booleanSupplier} + * returns true, or a specific waiting time elapses. The waiting time + * is 1 second scaled with the jtreg testing timeout factor. * - * @param s boolean supplier - * @return true if the {@code BooleanSupplier} returns true and false if - * the predefined waiting time elapsed before the count reaches zero. - * @throws InterruptedException if the current thread is interrupted while waiting + * @param booleanSupplier boolean supplier + * @return true if the {@code booleanSupplier} returns true, or false + * if did not complete after the specific waiting time. */ - public boolean await(BooleanSupplier s) { - o = null; // Keep reference to Object until now, to ensure the Cleaner - // doesn't count down the latch before await() is called. - for (int i = 0; i < 10; i++) { - if (s.getAsBoolean()) { - System.out.println("ForceGC condition met after System.gc() " + gcCount + " times"); + public static boolean wait(BooleanSupplier booleanSupplier) { + ReferenceQueue queue = new ReferenceQueue<>(); + Object obj = new Object(); + PhantomReference ref = new PhantomReference<>(obj, queue); + obj = null; + Reference.reachabilityFence(obj); + Reference.reachabilityFence(ref); + + int retries = (int)(Math.round(1000L * TIMEOUT_FACTOR) / 200); + for (; retries >= 0; retries--) { + if (booleanSupplier.getAsBoolean()) { return true; } - doit(i); + System.gc(); + try { - Thread.sleep(100); - } catch (InterruptedException e) { - throw new AssertionError("unexpected interrupted sleep", e); + // The remove() will always block for the specified milliseconds + // if the reference has already been removed from the queue. + // But it is fine. For most cases, the 1st GC is sufficient + // to trigger and complete the cleanup. + queue.remove(200L); + } catch (InterruptedException ie) { + // ignore, the loop will try again } } - System.out.println("ForceGC condition not met after System.gc() " + gcCount + " times"); - return false; + return booleanSupplier.getAsBoolean(); } } + From 0c8cf7a3bae6ffbf392fb48a3355d0b02aa2ebbc Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier Date: Mon, 9 Oct 2023 20:23:43 +0200 Subject: [PATCH 4/4] Backport f835aaafc7332d324ca9b08b2a34539fc1c573aa --- .../AwtListGarbageCollectionTest.java | 70 +++++++++++-------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/test/jdk/java/awt/List/ListGarbageCollectionTest/AwtListGarbageCollectionTest.java b/test/jdk/java/awt/List/ListGarbageCollectionTest/AwtListGarbageCollectionTest.java index 56ab900c93b..77fa7538aed 100644 --- a/test/jdk/java/awt/List/ListGarbageCollectionTest/AwtListGarbageCollectionTest.java +++ b/test/jdk/java/awt/List/ListGarbageCollectionTest/AwtListGarbageCollectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, 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 @@ -21,21 +21,28 @@ * questions. */ -/** +import java.awt.Frame; +import java.awt.List; +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; + +import jdk.test.lib.util.ForceGC; + +/* * @test * @key headful * @bug 8040076 * @summary AwtList not garbage collected - * @run main/othervm -Xmx100m AwtListGarbageCollectionTest + * @library /test/lib/ + * @build jdk.test.lib.util.ForceGC + * @run main/othervm -Xmx100m -Xlog:gc=debug AwtListGarbageCollectionTest */ +public class AwtListGarbageCollectionTest { -import java.awt.*; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.lang.ref.WeakReference; + private static final long ENQUEUE_TIMEOUT = 50; -public class AwtListGarbageCollectionTest { - public static void main(String[] args) { + public static void main(String[] args) throws InterruptedException { Frame frame = new Frame("List leak test"); try { test(frame); @@ -45,29 +52,32 @@ public static void main(String[] args) { } private static void test(Frame frame) { - WeakReference weakListRef = null; - try { - frame.setSize(300, 200); - frame.setVisible(true); + frame.setSize(300, 200); + frame.setVisible(true); - List strongListRef = new List(); - frame.add(strongListRef); - strongListRef.setMultipleMode(true); - frame.remove(strongListRef); - weakListRef = new WeakReference(strongListRef); - strongListRef = null; + List strongListRef = new List(); + frame.add(strongListRef); + strongListRef.setMultipleMode(true); + frame.remove(strongListRef); - //make out of memory to force gc - String veryLongString = new String(new char[100]); - while (true) { - veryLongString += veryLongString; - } - } catch (OutOfMemoryError e) { - if (weakListRef == null) { - throw new RuntimeException("Weak list ref wasn't created"); - } else if (weakListRef.get() != null) { - throw new RuntimeException("List wasn't garbage collected"); - } + final ReferenceQueue referenceQueue = new ReferenceQueue<>(); + final PhantomReference phantomListRef = + new PhantomReference<>(strongListRef, referenceQueue); + System.out.println("phantomListRef: " + phantomListRef); + + strongListRef = null; // Clear the strong reference + + System.out.println("Waiting for the reference to be cleared"); + if (!ForceGC.wait(() -> phantomListRef == remove(referenceQueue))) { + throw new RuntimeException("List wasn't garbage collected"); + } + } + + private static Reference remove(ReferenceQueue queue) { + try { + return queue.remove(ENQUEUE_TIMEOUT); + } catch (InterruptedException e) { + throw new RuntimeException(e); } } }