forked from I2P_Developers/i2p.i2p
* OOMListener: Dump threads on OOM
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
43
router/java/src/net/i2p/router/tasks/ThreadDump.java
Normal file
43
router/java/src/net/i2p/router/tasks/ThreadDump.java
Normal 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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user