/* * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Version 1.0, and under the Eclipse Public License, Version 1.0 * (http://h2database.com/html/license.html). * Initial Developer: H2 Group */ package org.h2.test.unit; import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.net.URLClassLoader; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; import java.util.ArrayList; import org.h2.test.TestBase; import org.h2.util.New; /** * Test that static references within the database engine don't reference the * class itself. For example, there is a leak if a class contains a static * reference to a stack trace. This was the case using the following * declaration: static EOFException EOF = new EOFException(). The way to solve * the problem is to not use such references, or to not fill in the stack trace * (which indirectly references the class loader). * * @author Erik Karlsson * @author Thomas Mueller */ public class TestClassLoaderLeak extends TestBase { /** * The name of this class (used by reflection). */ static final String CLASS_NAME = TestClassLoaderLeak.class.getName(); /** * Run just this test. * * @param a ignored */ public static void main(String... a) throws Exception { TestBase.createCaller().init().test(); } public void test() throws Exception { WeakReference<ClassLoader> ref = createClassLoader(); for (int i = 0; i < 10; i++) { System.gc(); Thread.sleep(10); } ClassLoader cl = ref.get(); assertTrue(cl == null); // fill the memory, so a heap dump is created // using -XX:+HeapDumpOnOutOfMemoryError // which can be analyzed using EclipseMAT // (check incoming references to TestClassLoader) boolean fillMemory = false; if (fillMemory) { ArrayList<byte[]> memory = New.arrayList(); for (int i = 0; i < Integer.MAX_VALUE; i++) { memory.add(new byte[1024]); } } DriverManager.registerDriver((Driver) Class.forName("org.h2.Driver").newInstance()); DriverManager.registerDriver((Driver) Class.forName("org.h2.upgrade.v1_1.Driver").newInstance()); } private static WeakReference<ClassLoader> createClassLoader() throws Exception { ClassLoader cl = new TestClassLoader(); Class<?> h2ConnectionTestClass = Class.forName(CLASS_NAME, true, cl); Method testMethod = h2ConnectionTestClass.getDeclaredMethod("runTest"); testMethod.setAccessible(true); testMethod.invoke(null); return new WeakReference<ClassLoader>(cl); } /** * This method is called using reflection. */ static void runTest() throws Exception { Class.forName("org.h2.Driver"); Class.forName("org.h2.upgrade.v1_1.Driver"); Driver d1 = DriverManager.getDriver("jdbc:h2:mem:test"); Driver d2 = DriverManager.getDriver("jdbc:h2v1_1:mem:test"); Connection connection; connection = DriverManager.getConnection("jdbc:h2:mem:test"); DriverManager.deregisterDriver(d1); DriverManager.deregisterDriver(d2); connection.close(); connection = null; } /** * The application class loader. */ private static class TestClassLoader extends URLClassLoader { public TestClassLoader() { super(((URLClassLoader) TestClassLoader.class.getClassLoader()).getURLs(), ClassLoader .getSystemClassLoader()); } // allows delegation of H2 to the AppClassLoader public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { if (!name.contains(CLASS_NAME) && !name.startsWith("org.h2.")) { return super.loadClass(name, resolve); } Class<?> c = findLoadedClass(name); if (c == null) { try { c = findClass(name); } catch (SecurityException e) { return super.loadClass(name, resolve); } catch (ClassNotFoundException e) { return super.loadClass(name, resolve); } if (resolve) { resolveClass(c); } } return c; } } }