30 * @build jdk.testlibrary.Utils JarUtils |
30 * @build jdk.testlibrary.Utils JarUtils |
31 * @build ClassForName ClassForNameLeak |
31 * @build ClassForName ClassForNameLeak |
32 * @run main/othervm/policy=test.policy -Djava.security.manager ClassForNameLeak |
32 * @run main/othervm/policy=test.policy -Djava.security.manager ClassForNameLeak |
33 */ |
33 */ |
34 |
34 |
|
35 import java.io.IOException; |
35 import java.lang.ref.PhantomReference; |
36 import java.lang.ref.PhantomReference; |
36 import java.lang.ref.Reference; |
37 import java.lang.ref.Reference; |
37 import java.lang.ref.ReferenceQueue; |
38 import java.lang.ref.ReferenceQueue; |
|
39 import java.net.MalformedURLException; |
38 import java.net.URL; |
40 import java.net.URL; |
39 import java.net.URLClassLoader; |
41 import java.net.URLClassLoader; |
40 import java.nio.file.FileSystems; |
|
41 import java.nio.file.Files; |
|
42 import java.nio.file.Path; |
42 import java.nio.file.Path; |
43 import java.nio.file.Paths; |
43 import java.nio.file.Paths; |
44 import java.util.List; |
44 import java.util.List; |
45 import java.util.concurrent.Callable; |
45 import java.util.concurrent.Callable; |
46 import java.util.concurrent.ExecutorService; |
46 import java.util.concurrent.ExecutorService; |
53 /* |
53 /* |
54 * Create .jar, load ClassForName from .jar using a URLClassLoader |
54 * Create .jar, load ClassForName from .jar using a URLClassLoader |
55 */ |
55 */ |
56 public class ClassForNameLeak { |
56 public class ClassForNameLeak { |
57 private static final long TIMEOUT = (long)(5000.0 * Utils.TIMEOUT_FACTOR); |
57 private static final long TIMEOUT = (long)(5000.0 * Utils.TIMEOUT_FACTOR); |
58 private static final String TESTCLASSES = System.getProperty("test.classes", "."); |
|
59 private static final String CLASSFILENAME = "ClassForName.class"; |
|
60 private static final int THREADS = 10; |
58 private static final int THREADS = 10; |
|
59 private static final Path jarFilePath = Paths.get("cfn.jar"); |
61 private static final ReferenceQueue<ClassLoader> rq = new ReferenceQueue<>(); |
60 private static final ReferenceQueue<ClassLoader> rq = new ReferenceQueue<>(); |
62 |
61 |
63 // Use a new classloader to load the ClassForName class, then run its |
62 static class TestLoader { |
64 // Runnable. |
63 private final PhantomReference<ClassLoader> ref; |
65 public static PhantomReference<ClassLoader> loadAndRun(Path jarFilePath) |
64 TestLoader() { |
66 throws Exception { |
65 this.ref = loadAndRun(); |
67 ClassLoader classLoader = new URLClassLoader( |
66 } |
68 new URL[]{jarFilePath.toUri().toURL()}) { |
|
69 @Override public String toString() { return "LeakedClassLoader"; } |
|
70 }; |
|
71 |
67 |
72 Class<?> loadClass = Class.forName("ClassForName", true, classLoader); |
68 // Use a new classloader to load the ClassForName class, then run its |
73 ((Runnable) loadClass.newInstance()).run(); |
69 // Runnable. |
|
70 PhantomReference<ClassLoader> loadAndRun() { |
|
71 try { |
|
72 ClassLoader classLoader = |
|
73 new URLClassLoader("LeakedClassLoader", |
|
74 new URL[]{jarFilePath.toUri().toURL()}, |
|
75 ClassLoader.getPlatformClassLoader()); |
74 |
76 |
75 PhantomReference<ClassLoader> ref = new PhantomReference<>(classLoader, rq); |
77 Class<?> loadClass = Class.forName("ClassForName", true, classLoader); |
76 System.out.println("returning phantom ref: " + ref + " to " + classLoader); |
78 ((Runnable) loadClass.newInstance()).run(); |
77 return ref; |
79 |
|
80 return new PhantomReference<>(classLoader, rq); |
|
81 } catch (MalformedURLException|ReflectiveOperationException e) { |
|
82 throw new RuntimeException(e); |
|
83 } |
|
84 } |
|
85 |
|
86 PhantomReference<ClassLoader> getRef() { |
|
87 return ref; |
|
88 } |
78 } |
89 } |
79 |
90 |
80 public static void main(final String[] args) throws Exception { |
91 public static void main(String... args) throws Exception { |
81 // Create a temporary .jar file containing ClassForName.class |
92 // create the JAR file |
82 Path testClassesDir = Paths.get(TESTCLASSES); |
93 setup(); |
83 Path jarFilePath = Files.createTempFile("cfn", ".jar"); |
|
84 JarUtils.createJarFile(jarFilePath, testClassesDir, CLASSFILENAME); |
|
85 jarFilePath.toFile().deleteOnExit(); |
|
86 |
|
87 // Remove the ClassForName.class file that jtreg built, to make sure |
|
88 // we're loading from the tmp .jar |
|
89 Path classFile = FileSystems.getDefault().getPath(TESTCLASSES, |
|
90 CLASSFILENAME); |
|
91 Files.delete(classFile); |
|
92 |
94 |
93 // Make simultaneous calls to the test method, to stress things a bit |
95 // Make simultaneous calls to the test method, to stress things a bit |
94 ExecutorService es = Executors.newFixedThreadPool(THREADS); |
96 ExecutorService es = Executors.newFixedThreadPool(THREADS); |
95 |
97 |
96 List<Callable<PhantomReference<ClassLoader>>> callables = |
98 List<Callable<TestLoader>> callables = |
97 Stream.generate(() -> { |
99 Stream.generate(() -> { |
98 Callable<PhantomReference<ClassLoader>> cprcl = () -> { |
100 Callable<TestLoader> cprcl = TestLoader::new; |
99 return loadAndRun(jarFilePath); |
|
100 }; |
|
101 return cprcl; |
101 return cprcl; |
102 }).limit(THREADS).collect(Collectors.toList()); |
102 }).limit(THREADS).collect(Collectors.toList()); |
103 |
103 |
104 List<Future<PhantomReference<ClassLoader>>> refs = es.invokeAll(callables); |
104 List<Future<TestLoader>> futures = es.invokeAll(callables); |
105 |
105 |
106 // Give the GC a chance to enqueue the PhantomReferences |
106 // Give the GC a chance to enqueue the PhantomReferences |
107 for (int i = 0; i < 10; i++) { |
107 for (int i = 0; i < 10; i++) { |
108 System.gc(); |
108 System.gc(); |
109 } |
109 } |
|
110 |
110 // Make sure all PhantomReferences to the leaked classloader are enqueued |
111 // Make sure all PhantomReferences to the leaked classloader are enqueued |
111 for (int j = 0; j < THREADS; j++) { |
112 for (int j = 0; j < futures.size(); j++) { |
112 Reference rmRef = rq.remove(TIMEOUT); |
113 Reference rmRef = rq.remove(TIMEOUT); |
113 if (rmRef == null) { |
114 if (rmRef == null) { |
114 throw new RuntimeException("ClassLoader was never enqueued!"); |
115 throw new RuntimeException("ClassLoader was never enqueued!"); |
115 } else { |
116 } else { |
116 System.out.println("Enqueued " + rmRef); |
117 System.out.println("Enqueued " + rmRef); |
117 } |
118 } |
118 } |
119 } |
119 System.out.println("All Classloaders successfully enqued"); |
120 es.shutdown(); |
|
121 System.out.println("All ClassLoaders successfully enqueued"); |
|
122 } |
|
123 |
|
124 private static final String CLASSFILENAME = "ClassForName.class"; |
|
125 private static void setup() throws IOException { |
|
126 String testclasses = System.getProperty("test.classes", "."); |
|
127 |
|
128 // Create a temporary .jar file containing ClassForName.class |
|
129 Path testClassesDir = Paths.get(testclasses); |
|
130 JarUtils.createJarFile(jarFilePath, testClassesDir, CLASSFILENAME); |
120 } |
131 } |
121 } |
132 } |