test/jdk/java/lang/ClassLoader/forNameLeak/ClassForNameLeak.java
changeset 48849 64a52906b71f
parent 47216 71c04702a3d5
child 51754 594919232b8f
equal deleted inserted replaced
48848:81f3a5eaecb0 48849:64a52906b71f
     1 /*
     1 /*
     2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.
     7  * published by the Free Software Foundation.
    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 }