Skip to content

Instantly share code, notes, and snippets.

@metlos
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save metlos/59c9cf891482f14d1784 to your computer and use it in GitHub Desktop.
Save metlos/59c9cf891482f14d1784 to your computer and use it in GitHub Desktop.
/*-------------------------------------------------------------------------
*
* Copyright (c) 2004-2014, PostgreSQL Global Development Group
*
*
*-------------------------------------------------------------------------
*/
package org.postgresql.test.leaks;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashSet;
import java.util.Set;
import junit.framework.TestCase;
import junit.framework.TestSuite;
public class ClassLoaderLeakTest extends TestCase
{
private static class DriverIsolatingClassLoader extends URLClassLoader
{
private static URL artifactJar()
{
// this assumes <sysproperty key="driverArtifact" value="${artifact.jar}"/> in the <junit> definition in build.xml
String jar = System.getProperty("driverArtifact");
try
{
return new URL("file:./" + jar);
}
catch (MalformedURLException e)
{
throw new IllegalArgumentException(e);
}
}
public DriverIsolatingClassLoader()
{
super(new URL[] {artifactJar()});
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
if (name.startsWith("org.postgresql"))
{
Class<?> cls = super.findClass(name);
if (resolve)
{
super.resolveClass(cls);
}
return cls;
}
else
{
return super.loadClass(name, resolve);
}
}
}
public ClassLoaderLeakTest(String name)
{
super(name);
}
public void testPermgenRemainsStableAfterDriverReloaded() throws Exception {
Set<WeakReference<Class<?>>> driverRefs = new HashSet<WeakReference<Class<?>>>();
// load the driver 10 times...
for (int i = 0; i < 10; ++i)
{
WeakReference<Class<?>> driverRef = new WeakReference<Class<?>>(
Class.forName("org.postgresql.Driver", true, new DriverIsolatingClassLoader()));
driverRefs.add(driverRef);
}
// now generate lots and lots of short lived objects to force the drivers out of the heap
for (long i = 0; i < 10000000000l; ++i)
{
new Object();
}
System.gc();
// check how many driver refs survived...
// note that the leak would occur if the loading of the driver left some "dangling" thread running.
// The threads keep a reference to the protection domain they're running with and a protection domain keeps a
// a reference to the class loader. The org.postgresql.Driver class had a static cancelTimer Timer field, that
// created a thread that was never stopped. This thread was created from the classloader that loaded the driver
// class.
// In a containerized environment (web containers, etc.) this is a problem leading to eventual depletion of
// permgen due to high number of classes being loaded (and never unloaded again).
for (WeakReference<Class<?>> ref : driverRefs) {
assert ref.get() == null : "Some of the driver references survived GC. Leak?";
}
}
}
@metlos
Copy link
Author

metlos commented Dec 3, 2014

To explain line 49 - https://gist.github.com/metlos/59c9cf891482f14d1784#file-classloaderleaktest-java-L49. This basically makes sure that the classes from the plugin are always loaded anew even if the class already exists in the parent classloader.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment