1 /* 2 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 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 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @bug 6799583 27 * 28 * @summary Test verifes that LogManager shutdown hook does not cause 29 * an application classloader leaks. 30 * 31 * @run main/othervm ClassLoaderLeakTest 32 */ 33 34 import java.io.File; 35 import java.lang.ref.WeakReference; 36 import java.lang.reflect.Constructor; 37 import java.lang.reflect.Method; 38 import java.net.MalformedURLException; 39 import java.net.URL; 40 import java.net.URLClassLoader; 41 import java.util.concurrent.CountDownLatch; 42 import java.util.logging.Logger; 43 import java.util.logging.Logger; 44 45 public class ClassLoaderLeakTest { 46 47 private static CountDownLatch doneSignal; 48 private static CountDownLatch launchSignal; 49 private static ThreadGroup appsThreadGroup; 50 private static Throwable launchFailure = null; 51 52 public static void main(String[] args) { 53 appsThreadGroup = new ThreadGroup("MyAppsThreadGroup"); 54 doneSignal = new CountDownLatch(1); 55 launchSignal = new CountDownLatch(1); 56 57 Runnable launcher = new Runnable() { 58 public void run() { 59 try { 60 ClassLoader cl = 61 Thread.currentThread().getContextClassLoader(); 62 Class appMain = cl.loadClass("AppTest"); 63 Method launch = 64 appMain.getDeclaredMethod("launch", doneSignal.getClass()); 65 66 Constructor c = appMain.getConstructor(); 67 68 Object o = c.newInstance(); 69 70 launch.invoke(o, doneSignal); 71 72 } catch (Throwable e) { 73 launchFailure = e; 74 } finally { 75 launchSignal.countDown(); 76 } 77 } 78 }; 79 80 /* prepare test class loader */ 81 URL pwd = null; 82 try { 83 84 pwd = new File(System.getProperty("test.classes",".")).toURL(); 85 } catch (MalformedURLException e) { 86 throw new RuntimeException("Test failed.", e); 87 } 88 URL[] urls = new URL[] { pwd }; 89 90 MyClassLoader appClassLoader = new MyClassLoader(urls, "test0"); 91 WeakReference<MyClassLoader> ref = 92 new WeakReference<>(appClassLoader); 93 94 95 Thread appThread = new Thread(appsThreadGroup, launcher, "AppThread-0"); 96 appThread.setContextClassLoader(appClassLoader); 97 98 appThread.start(); 99 appClassLoader = null; 100 launcher = null; 101 appThread = null; 102 103 /* wait for laucnh completion */ 104 try { 105 launchSignal.await(); 106 } catch (InterruptedException e) { 107 } 108 109 /* check if launch failed */ 110 if (launchFailure != null) { 111 throw new RuntimeException("Test failed.", launchFailure); 112 } 113 114 /* wait for test app excution completion */ 115 try { 116 doneSignal.await(); 117 } catch (InterruptedException e) { 118 } 119 120 /* give a chence to GC */ 121 waitAndGC(5); 122 123 if (ref.get() != null) { 124 throw new RuntimeException("Test failed: classloader is still alive"); 125 } 126 127 System.out.println("Test passed."); 128 } 129 130 private static class MyClassLoader extends URLClassLoader { 131 132 private static boolean verbose = 133 Boolean.getBoolean("verboseClassLoading"); 134 private String uniqClassName; 135 136 public MyClassLoader(URL[] urls, String uniq) { 137 super(urls); 138 139 uniqClassName = uniq; 140 } 141 142 public Class loadClass(String name) throws ClassNotFoundException { 143 if (verbose) { 144 System.out.printf("%s: load class %s\n", uniqClassName, name); 145 } 146 if (uniqClassName.equals(name)) { 147 return Object.class; 148 } 149 return super.loadClass(name); 150 } 151 152 public String toString() { 153 return "MyClassLoader(" + uniqClassName + ")"; 154 } 155 } 156 157 private static void waitAndGC(int sec) { 158 int cnt = sec; 159 System.out.print("Wait "); 160 while (cnt-- > 0) { 161 try { 162 Thread.sleep(1000); 163 } catch (InterruptedException e) { 164 } 165 // do GC every 3 seconds 166 if (cnt % 3 == 2) { 167 System.gc(); 168 System.out.print("+"); 169 } else { 170 System.out.print("."); 171 } 172 //checkErrors(); 173 } 174 System.out.println(""); 175 } 176 } 177 178 179 class AppTest { 180 public AppTest() { 181 182 } 183 184 public void launch(CountDownLatch done) { 185 Logger log = Logger.getLogger("app_test_logger"); 186 log.fine("Test app is launched"); 187 188 done.countDown(); 189 } 190 }