提交 40fcee2d authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Use single shared instance of OnExitDatabaseCloser to avoid memory leaks

上级 b87d4a71
...@@ -168,7 +168,6 @@ public class Database implements DataHandler { ...@@ -168,7 +168,6 @@ public class Database implements DataHandler {
private boolean referentialIntegrity = true; private boolean referentialIntegrity = true;
/** ie. the MVCC setting */ /** ie. the MVCC setting */
private boolean multiVersion; private boolean multiVersion;
private OnExitDatabaseCloser closeOnExit;
private Mode mode = Mode.getRegular(); private Mode mode = Mode.getRegular();
private boolean multiThreaded; private boolean multiThreaded;
private int maxOperationMemory = private int maxOperationMemory =
...@@ -289,17 +288,7 @@ public class Database implements DataHandler { ...@@ -289,17 +288,7 @@ public class Database implements DataHandler {
try { try {
open(traceLevelFile, traceLevelSystemOut, ci); open(traceLevelFile, traceLevelSystemOut, ci);
if (closeAtVmShutdown) { if (closeAtVmShutdown) {
try { OnExitDatabaseCloser.register(this);
closeOnExit = new OnExitDatabaseCloser(this);
} catch (IllegalStateException e) {
// shutdown in progress - just don't register the handler
// (maybe an application wants to write something into a
// database at shutdown time)
} catch (SecurityException e) {
// applets may not do that - ignore
// Google App Engine doesn't allow
// to instantiate classes that extend Thread
}
} }
} catch (Throwable e) { } catch (Throwable e) {
if (e instanceof OutOfMemoryError) { if (e instanceof OutOfMemoryError) {
...@@ -1357,10 +1346,7 @@ public class Database implements DataHandler { ...@@ -1357,10 +1346,7 @@ public class Database implements DataHandler {
} }
trace.info("closed"); trace.info("closed");
traceSystem.close(); traceSystem.close();
if (closeOnExit != null) { OnExitDatabaseCloser.unregister(this);
closeOnExit.reset();
closeOnExit = null;
}
if (deleteFilesOnDisconnect && persistent) { if (deleteFilesOnDisconnect && persistent) {
deleteFilesOnDisconnect = false; deleteFilesOnDisconnect = false;
try { try {
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
*/ */
package org.h2.engine; package org.h2.engine;
import java.lang.ref.WeakReference; import java.util.WeakHashMap;
import org.h2.message.Trace; import org.h2.message.Trace;
...@@ -14,35 +14,49 @@ import org.h2.message.Trace; ...@@ -14,35 +14,49 @@ import org.h2.message.Trace;
*/ */
class OnExitDatabaseCloser extends Thread { class OnExitDatabaseCloser extends Thread {
private final Trace trace; private static final WeakHashMap<Database, Void> DATABASES = new WeakHashMap<>();
private volatile WeakReference<Database> databaseRef;
OnExitDatabaseCloser(Database db) { private static OnExitDatabaseCloser INSTANCE;
databaseRef = new WeakReference<>(db);
trace = db.getTrace(Trace.DATABASE); static synchronized void register(Database db) {
Runtime.getRuntime().addShutdownHook(this); DATABASES.put(db, null);
if (INSTANCE == null) {
try {
// Assign INSTANCE unconditionally to avoid further attempts to register a
// shutdown hook in case of exception.
Runtime.getRuntime().addShutdownHook(INSTANCE = new OnExitDatabaseCloser());
} catch (IllegalStateException e) {
// shutdown in progress - just don't register the handler
// (maybe an application wants to write something into a
// database at shutdown time)
} catch (SecurityException e) {
// applets may not do that - ignore
// Google App Engine doesn't allow
// to instantiate classes that extend Thread
}
}
} }
/** static synchronized void unregister(Database db) {
* Stop and disable the database closer. This method is called after the DATABASES.remove(db);
* database has been closed. if (DATABASES.isEmpty() && INSTANCE != null) {
*/ try {
void reset() { Runtime.getRuntime().removeShutdownHook(INSTANCE);
databaseRef = null; } catch (IllegalStateException e) {
try { // ignore
Runtime.getRuntime().removeShutdownHook(this); } catch (SecurityException e) {
} catch (IllegalStateException e) { // applets may not do that - ignore
// ignore }
} catch (SecurityException e) { INSTANCE = null;
// applets may not do that - ignore
} }
} }
private OnExitDatabaseCloser() {
}
@Override @Override
public void run() { public void run() {
Database database; for (Database database : DATABASES.keySet()) {
WeakReference<Database> ref = databaseRef;
if (ref != null && (database = ref.get()) != null) {
try { try {
database.close(true); database.close(true);
} catch (RuntimeException e) { } catch (RuntimeException e) {
...@@ -50,7 +64,7 @@ class OnExitDatabaseCloser extends Thread { ...@@ -50,7 +64,7 @@ class OnExitDatabaseCloser extends Thread {
// if loading classes is no longer allowed // if loading classes is no longer allowed
// it would throw an IllegalStateException // it would throw an IllegalStateException
try { try {
trace.error(e, "could not close the database"); database.getTrace(Trace.DATABASE).error(e, "could not close the database");
// if this was successful, we ignore the exception // if this was successful, we ignore the exception
// otherwise not // otherwise not
} catch (Throwable e2) { } catch (Throwable e2) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论