提交 cbaab660 authored 作者: Thomas Mueller's avatar Thomas Mueller

There was a classloader memory leak problem because a class contained a static…

There was a classloader memory leak problem because a class contained a static references to an exception (including stack trace).
上级 0453eaba
...@@ -17,7 +17,9 @@ Change Log ...@@ -17,7 +17,9 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>Split file system: truncating a file now deletes the parts in reverse order, <ul><li>There was a classloader memory leak problem because a class contained a static
references to an exception (including stack trace).
</li><li>Split file system: truncating a file now deletes the parts in reverse order,
so that the file list is consistent if the process is interrupted while truncating. so that the file list is consistent if the process is interrupted while truncating.
</li><li>The H2 JDBC client can now be used in an unsigned Applet. </li><li>The H2 JDBC client can now be used in an unsigned Applet.
The problem was that System.getProperty throws a SecurityException, which is now ignored. The problem was that System.getProperty throws a SecurityException, which is now ignored.
......
...@@ -18,7 +18,6 @@ import org.h2.util.IOUtils; ...@@ -18,7 +18,6 @@ import org.h2.util.IOUtils;
*/ */
public class DataReader extends Reader { public class DataReader extends Reader {
private static final EOFException EOF = new EOFException();
private InputStream in; private InputStream in;
/** /**
...@@ -38,7 +37,7 @@ public class DataReader extends Reader { ...@@ -38,7 +37,7 @@ public class DataReader extends Reader {
public byte readByte() throws IOException { public byte readByte() throws IOException {
int x = in.read(); int x = in.read();
if (x < 0) { if (x < 0) {
throw EOF; throw new FastEOFException();
} }
return (byte) x; return (byte) x;
} }
...@@ -120,7 +119,7 @@ public class DataReader extends Reader { ...@@ -120,7 +119,7 @@ public class DataReader extends Reader {
public void readFully(byte[] buff, int offset, int len) throws IOException { public void readFully(byte[] buff, int offset, int len) throws IOException {
int got = IOUtils.readFully(in, buff, offset, len); int got = IOUtils.readFully(in, buff, offset, len);
if (got < len) { if (got < len) {
throw EOF; throw new FastEOFException();
} }
} }
...@@ -174,4 +173,19 @@ public class DataReader extends Reader { ...@@ -174,4 +173,19 @@ public class DataReader extends Reader {
} }
} }
/**
* Constructing such an EOF exception is fast, because the stack trace is
* not filled in. If used in a static context, this will also avoid
* classloader memory leaks.
*/
static class FastEOFException extends EOFException {
private static final long serialVersionUID = 1L;
public synchronized Throwable fillInStackTrace() {
return null;
}
}
} }
/*
* 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 {
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)
if (false) {
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);
}
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;
}
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论