* OOMListener: Dump threads on OOM

This commit is contained in:
zzz
2012-10-02 12:40:00 +00:00
parent 07b3c8a7b4
commit 93039a6813
3 changed files with 77 additions and 28 deletions

View File

@@ -1,5 +1,7 @@
package net.i2p.router.tasks;
import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.util.I2PThread;
@@ -12,30 +14,43 @@ import net.i2p.util.Log;
*/
public class OOMListener implements I2PThread.OOMEventListener {
private final RouterContext _context;
private final AtomicBoolean _wasCalled = new AtomicBoolean();
public OOMListener(RouterContext ctx) {
_context = ctx;
}
public void outOfMemory(OutOfMemoryError oom) {
// boost priority to help us shut down
Thread.currentThread().setPriority(Thread.MAX_PRIORITY - 1);
Router.clearCaches();
Log log = _context.logManager().getLog(Router.class);
log.log(Log.CRIT, "Thread ran out of memory, shutting down I2P", oom);
// prevent multiple parallel shutdowns (when you OOM, you OOM a lot...)
if (_context.router().isFinalShutdownInProgress())
return;
for (int i = 0; i < 5; i++) { // try this 5 times, in case it OOMs
try {
log.log(Log.CRIT, "free mem: " + Runtime.getRuntime().freeMemory() +
" total mem: " + Runtime.getRuntime().totalMemory());
break; // w00t
} catch (OutOfMemoryError oome) {
// gobble
}
}
log.log(Log.CRIT, "To prevent future shutdowns, increase wrapper.java.maxmemory in $I2P/wrapper.config");
try {
// prevent multiple parallel shutdowns (when you OOM, you OOM a lot...)
if (_context.router().isFinalShutdownInProgress())
return;
} catch (OutOfMemoryError oome) {}
try {
// Only do this once
if (_wasCalled.getAndSet(true))
return;
} catch (OutOfMemoryError oome) {}
try {
// boost priority to help us shut down
Thread.currentThread().setPriority(Thread.MAX_PRIORITY - 1);
} catch (OutOfMemoryError oome) {}
try {
Router.clearCaches();
} catch (OutOfMemoryError oome) {}
Log log = null;
try {
log = _context.logManager().getLog(Router.class);
log.log(Log.CRIT, "Thread ran out of memory, shutting down I2P", oom);
log.log(Log.CRIT, "free mem: " + Runtime.getRuntime().freeMemory() +
" total mem: " + Runtime.getRuntime().totalMemory());
if (_context.hasWrapper())
log.log(Log.CRIT, "To prevent future shutdowns, increase wrapper.java.maxmemory in $I2P/wrapper.config");
} catch (OutOfMemoryError oome) {}
try {
ThreadDump.dump(_context, 1);
} catch (OutOfMemoryError oome) {}
_context.router().shutdown(Router.EXIT_OOM);
}
}

View File

@@ -113,16 +113,7 @@ public class RouterWatchdog implements Runnable {
// This works on linux...
// It won't on windows, and we can't call i2prouter.bat either, it does something
// completely different...
if (_context.hasWrapper() && !SystemVersion.isWindows()) {
ShellCommand sc = new ShellCommand();
File i2pr = new File(_context.getBaseDir(), "i2prouter");
String[] args = new String[2];
args[0] = i2pr.getAbsolutePath();
args[1] = "dump";
boolean success = sc.executeSilentAndWaitTimed(args, 10);
if (success)
_log.log(Log.CRIT, "Threads dumped to wrapper log");
}
ThreadDump.dump(_context, 10);
}
}
}

View File

@@ -0,0 +1,43 @@
package net.i2p.router.tasks;
import java.io.File;
import net.i2p.I2PAppContext;
import net.i2p.util.ShellCommand;
import net.i2p.util.Log;
import net.i2p.util.SystemVersion;
/**
* Only works with wrapper on non-windows platforms
*
* @since 0.9.3 moved from RouterWatchdog
*/
abstract class ThreadDump {
/**
* Signal the wrapper to asynchronously dump threads to wrapper.log.
* It waits for the signal to complete (which should be fast)
* but does not wait for the dump itself.
*
* @param secondsToWait if <= 0, don't wait
* @return success, false if windows or no wrapper, true if secondsToWait <= 0,
false if timed out, dump result otherwise
*/
public static boolean dump(I2PAppContext context, int secondsToWait) {
if (SystemVersion.isWindows() || !context.hasWrapper())
return false;
ShellCommand sc = new ShellCommand();
File i2pr = new File(context.getBaseDir(), "i2prouter");
String[] args = new String[2];
args[0] = i2pr.getAbsolutePath();
args[1] = "dump";
boolean success = sc.executeSilentAndWaitTimed(args, secondsToWait);
if (secondsToWait <= 0)
success = true;
if (success) {
Log log = context.logManager().getLog(ThreadDump.class);
log.log(Log.CRIT, "Threads dumped to wrapper log");
}
return success;
}
}