Skip to content

Commit dc3abee

Browse files
author
Andrew Lu
committedJul 4, 2024
8309241: ClassForNameLeak fails intermittently as the class loader hasn't been unloaded
Backport-of: a23bbea9593a85a9d60431e68805efa6f960a4d4
1 parent b555bbb commit dc3abee

File tree

1 file changed

+29
-43
lines changed

1 file changed

+29
-43
lines changed
 

‎test/jdk/java/lang/ClassLoader/forNameLeak/ClassForNameLeak.java

+29-43
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -28,15 +28,14 @@
2828
* from a custom classloader.
2929
* @library /test/lib
3030
* @build jdk.test.lib.Utils
31+
* jdk.test.lib.util.ForceGC
3132
* jdk.test.lib.util.JarUtils
3233
* @build ClassForName ClassForNameLeak
3334
* @run main/othervm/policy=test.policy -Djava.security.manager ClassForNameLeak
3435
*/
3536

3637
import java.io.IOException;
37-
import java.lang.ref.PhantomReference;
38-
import java.lang.ref.Reference;
39-
import java.lang.ref.ReferenceQueue;
38+
import java.lang.ref.WeakReference;
4039
import java.net.MalformedURLException;
4140
import java.net.URL;
4241
import java.net.URLClassLoader;
@@ -51,6 +50,7 @@
5150
import java.util.stream.Stream;
5251

5352
import jdk.test.lib.Utils;
53+
import jdk.test.lib.util.ForceGC;
5454
import jdk.test.lib.util.JarUtils;
5555

5656
/*
@@ -60,35 +60,32 @@ public class ClassForNameLeak {
6060
private static final long TIMEOUT = (long)(5000.0 * Utils.TIMEOUT_FACTOR);
6161
private static final int THREADS = 10;
6262
private static final Path jarFilePath = Paths.get("cfn.jar");
63-
private static final ReferenceQueue<ClassLoader> rq = new ReferenceQueue<>();
6463

65-
static class TestLoader {
66-
private final PhantomReference<ClassLoader> ref;
67-
TestLoader() {
68-
this.ref = loadAndRun();
69-
}
70-
71-
// Use a new classloader to load the ClassForName class, then run its
72-
// Runnable.
73-
PhantomReference<ClassLoader> loadAndRun() {
64+
static class TestLoader extends URLClassLoader {
65+
static URL[] toURLs() {
7466
try {
75-
ClassLoader classLoader =
76-
new URLClassLoader("LeakedClassLoader",
77-
new URL[]{jarFilePath.toUri().toURL()},
78-
ClassLoader.getPlatformClassLoader());
79-
80-
Class<?> loadClass = Class.forName("ClassForName", true, classLoader);
81-
((Runnable) loadClass.newInstance()).run();
82-
83-
return new PhantomReference<>(classLoader, rq);
84-
} catch (MalformedURLException|ReflectiveOperationException e) {
67+
return new URL[]{jarFilePath.toUri().toURL()};
68+
} catch (MalformedURLException e) {
8569
throw new RuntimeException(e);
8670
}
8771
}
8872

89-
PhantomReference<ClassLoader> getRef() {
90-
return ref;
73+
TestLoader() {
74+
super("LeakedClassLoader", toURLs(), ClassLoader.getPlatformClassLoader());
75+
}
76+
}
77+
78+
// Use a new classloader to load the ClassForName class, then run its
79+
// Runnable.
80+
static WeakReference<TestLoader> loadAndRun() {
81+
TestLoader classLoader = new TestLoader();
82+
try {
83+
Class<?> loadClass = Class.forName("ClassForName", true, classLoader);
84+
((Runnable) loadClass.newInstance()).run();
85+
} catch (ReflectiveOperationException ex) {
86+
throw new RuntimeException(ex);
9187
}
88+
return new WeakReference<>(classLoader);
9289
}
9390

9491
public static void main(String... args) throws Exception {
@@ -98,30 +95,19 @@ public static void main(String... args) throws Exception {
9895
// Make simultaneous calls to the test method, to stress things a bit
9996
ExecutorService es = Executors.newFixedThreadPool(THREADS);
10097

101-
List<Callable<TestLoader>> callables =
98+
List<Callable<WeakReference<TestLoader>>> callables =
10299
Stream.generate(() -> {
103-
Callable<TestLoader> cprcl = TestLoader::new;
100+
Callable<WeakReference<TestLoader>> cprcl = ClassForNameLeak::loadAndRun;
104101
return cprcl;
105102
}).limit(THREADS).collect(Collectors.toList());
106103

107-
List<Future<TestLoader>> futures = es.invokeAll(callables);
108-
109-
// Give the GC a chance to enqueue the PhantomReferences
110-
for (int i = 0; i < 10; i++) {
111-
System.gc();
112-
}
113-
114-
// Make sure all PhantomReferences to the leaked classloader are enqueued
115-
for (int j = 0; j < futures.size(); j++) {
116-
Reference rmRef = rq.remove(TIMEOUT);
117-
if (rmRef == null) {
118-
throw new RuntimeException("ClassLoader was never enqueued!");
119-
} else {
120-
System.out.println("Enqueued " + rmRef);
104+
for (Future<WeakReference<TestLoader>> future : es.invokeAll(callables)) {
105+
WeakReference<TestLoader> ref = future.get();
106+
if (!ForceGC.wait(() -> ref.refersTo(null))) {
107+
throw new RuntimeException(ref.get() + " not unloaded");
121108
}
122109
}
123110
es.shutdown();
124-
System.out.println("All ClassLoaders successfully enqueued");
125111
}
126112

127113
private static final String CLASSFILENAME = "ClassForName.class";

1 commit comments

Comments
 (1)

openjdk-notifier[bot] commented on Jul 4, 2024

@openjdk-notifier[bot]
Please sign in to comment.