From a23bbea9593a85a9d60431e68805efa6f960a4d4 Mon Sep 17 00:00:00 2001 From: Mandy Chung Date: Fri, 2 Jun 2023 18:12:19 +0000 Subject: [PATCH] 8309241: ClassForNameLeak fails intermittently as the class loader hasn't been unloaded Reviewed-by: dnsimon, bchristi --- .../forNameLeak/ClassForNameLeak.java | 72 ++++++++----------- 1 file changed, 29 insertions(+), 43 deletions(-) diff --git a/test/jdk/java/lang/ClassLoader/forNameLeak/ClassForNameLeak.java b/test/jdk/java/lang/ClassLoader/forNameLeak/ClassForNameLeak.java index a0374522567..045e26bf3c3 100644 --- a/test/jdk/java/lang/ClassLoader/forNameLeak/ClassForNameLeak.java +++ b/test/jdk/java/lang/ClassLoader/forNameLeak/ClassForNameLeak.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -28,15 +28,14 @@ * from a custom classloader. * @library /test/lib * @build jdk.test.lib.Utils + * jdk.test.lib.util.ForceGC * jdk.test.lib.util.JarUtils * @build ClassForName ClassForNameLeak * @run main/othervm/policy=test.policy -Djava.security.manager ClassForNameLeak */ import java.io.IOException; -import java.lang.ref.PhantomReference; -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; @@ -51,6 +50,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.test.lib.Utils; +import jdk.test.lib.util.ForceGC; import jdk.test.lib.util.JarUtils; /* @@ -60,37 +60,34 @@ public class ClassForNameLeak { private static final long TIMEOUT = (long)(5000.0 * Utils.TIMEOUT_FACTOR); private static final int THREADS = 10; private static final Path jarFilePath = Paths.get("cfn.jar"); - private static final ReferenceQueue rq = new ReferenceQueue<>(); - static class TestLoader { - private final PhantomReference ref; - TestLoader() { - this.ref = loadAndRun(); - } - - // Use a new classloader to load the ClassForName class, then run its - // Runnable. - PhantomReference loadAndRun() { + static class TestLoader extends URLClassLoader { + static URL[] toURLs() { try { - ClassLoader classLoader = - new URLClassLoader("LeakedClassLoader", - new URL[]{jarFilePath.toUri().toURL()}, - ClassLoader.getPlatformClassLoader()); - - Class loadClass = Class.forName("ClassForName", true, classLoader); - ((Runnable) loadClass.newInstance()).run(); - - return new PhantomReference<>(classLoader, rq); - } catch (MalformedURLException|ReflectiveOperationException e) { + return new URL[]{jarFilePath.toUri().toURL()}; + } catch (MalformedURLException e) { throw new RuntimeException(e); } } - PhantomReference getRef() { - return ref; + TestLoader() { + super("LeakedClassLoader", toURLs(), ClassLoader.getPlatformClassLoader()); } } + // Use a new classloader to load the ClassForName class, then run its + // Runnable. + static WeakReference loadAndRun() { + TestLoader classLoader = new TestLoader(); + try { + Class loadClass = Class.forName("ClassForName", true, classLoader); + ((Runnable) loadClass.newInstance()).run(); + } catch (ReflectiveOperationException ex) { + throw new RuntimeException(ex); + } + return new WeakReference<>(classLoader); + } + public static void main(String... args) throws Exception { // create the JAR file setup(); @@ -98,30 +95,19 @@ public class ClassForNameLeak { // Make simultaneous calls to the test method, to stress things a bit ExecutorService es = Executors.newFixedThreadPool(THREADS); - List> callables = + List>> callables = Stream.generate(() -> { - Callable cprcl = TestLoader::new; + Callable> cprcl = ClassForNameLeak::loadAndRun; return cprcl; }).limit(THREADS).collect(Collectors.toList()); - List> futures = es.invokeAll(callables); - - // Give the GC a chance to enqueue the PhantomReferences - for (int i = 0; i < 10; i++) { - System.gc(); - } - - // Make sure all PhantomReferences to the leaked classloader are enqueued - for (int j = 0; j < futures.size(); j++) { - Reference rmRef = rq.remove(TIMEOUT); - if (rmRef == null) { - throw new RuntimeException("ClassLoader was never enqueued!"); - } else { - System.out.println("Enqueued " + rmRef); + for (Future> future : es.invokeAll(callables)) { + WeakReference ref = future.get(); + if (!ForceGC.wait(() -> ref.refersTo(null))) { + throw new RuntimeException(ref.get() + " not unloaded"); } } es.shutdown(); - System.out.println("All ClassLoaders successfully enqueued"); } private static final String CLASSFILENAME = "ClassForName.class";