提交 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
<h1>Change Log</h1>
<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.
</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.
......
......@@ -18,7 +18,6 @@ import org.h2.util.IOUtils;
*/
public class DataReader extends Reader {
private static final EOFException EOF = new EOFException();
private InputStream in;
/**
......@@ -38,7 +37,7 @@ public class DataReader extends Reader {
public byte readByte() throws IOException {
int x = in.read();
if (x < 0) {
throw EOF;
throw new FastEOFException();
}
return (byte) x;
}
......@@ -120,7 +119,7 @@ public class DataReader extends Reader {
public void readFully(byte[] buff, int offset, int len) throws IOException {
int got = IOUtils.readFully(in, buff, offset, len);
if (got < len) {
throw EOF;
throw new FastEOFException();
}
}
......@@ -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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论