diff --git a/router/java/src/net/i2p/router/tasks/OOMListener.java b/router/java/src/net/i2p/router/tasks/OOMListener.java index 6fc9b92cd..a5e4ea49e 100644 --- a/router/java/src/net/i2p/router/tasks/OOMListener.java +++ b/router/java/src/net/i2p/router/tasks/OOMListener.java @@ -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); } } diff --git a/router/java/src/net/i2p/router/tasks/RouterWatchdog.java b/router/java/src/net/i2p/router/tasks/RouterWatchdog.java index 775a2c88e..233c3bd7a 100644 --- a/router/java/src/net/i2p/router/tasks/RouterWatchdog.java +++ b/router/java/src/net/i2p/router/tasks/RouterWatchdog.java @@ -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); } } } diff --git a/router/java/src/net/i2p/router/tasks/ThreadDump.java b/router/java/src/net/i2p/router/tasks/ThreadDump.java new file mode 100644 index 000000000..c5d0b9481 --- /dev/null +++ b/router/java/src/net/i2p/router/tasks/ThreadDump.java @@ -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; + } +}