我听说过如何最好地解决这个问题的矛盾,我陷入了以下困境:
我一直都知道最好的做法是让他们离开以便JVM可以死掉,因为JVM在那时处于不一致的状态,但这似乎不起作用。
答案 0 :(得分:42)
OutOfMemoryError
就像任何其他错误一样。如果它从Thread.run()
中逃脱,将导致线程死亡。而已。此外,当线程死亡时,它不再是GC根,因此仅由此线程保留的所有引用都有资格进行垃圾回收。这意味着JVM很可能从OOME恢复。
如果你想要杀死你的JVM,不管是因为你怀疑它可能处于不一致的状态,请将其添加到java
选项中:
-XX:OnOutOfMemoryError="kill -9 %p"
%p
是当前的Java进程PID占位符。其余的是自我解释。
当然你也可以尝试捕捉OutOfMemoryError
并以某种方式处理它。但这很棘手。
答案 1 :(得分:29)
对于版本8u92,Oracle JDK中现在有一个JVM选项,可以在发生OutOfMemoryError时退出JVM:
ExitOnOutOfMemoryError - 启用此选项时,JVM会在第一次出现内存不足错误时退出。如果您更喜欢重新启动JVM实例而不是处理内存不足错误,则可以使用它。
答案 2 :(得分:26)
在Java版本8u92中,VM参数
-XX:+ExitOnOutOfMemoryError
-XX:+CrashOnOutOfMemoryError
已添加,请参阅release notes。
<强> ExitOnOutOfMemoryError 强>
启用此选项后,JVM将退出 第一次出现内存不足错误。如果你可以使用它 更喜欢重新启动JVM的实例而不是处理掉 记忆错误。<强> CrashOnOutOfMemoryError 强>
如果启用此选项,则启用此选项 发生内存不足错误,JVM崩溃并生成文本和 二进制崩溃文件。
增强请求:JDK-8138745(参数命名错误JDK-8154713,ExitOnOutOfMemoryError
而不是ExitOnOutOfMemory
)
答案 3 :(得分:5)
一旦错误发生,您可以强制程序以多种方式终止。像其他人建议的那样,如果需要,您可以捕获错误并在此之后执行System.exit。 但我建议您也使用-XX:+ HeapDumpOnOutOfMemoryError,这样JVM将在生成事件后使用应用程序的内容创建内存转储文件。您将使用配置文件,我建议您使用Eclipse MAT来调查图像。通过这种方式,您可以快速找到问题的原因,并做出正确的反应。如果您不使用Eclipse,则可以将Eclipse MAT用作独立产品,请参阅:http://wiki.eclipse.org/index.php/MemoryAnalyzer。
答案 4 :(得分:4)
如果要关闭程序,请查看命令行上的-XX:OnOutOfMemoryError="<cmd args>;<cmd args>"
(documented here)选项。只需将其指向应用程序的kill脚本即可。
一般情况下,如果不重新启动应用程序,我就没有任何运气可以优雅地处理此错误。总是有某种角落滑落,所以我个人建议确实停止你的申请,但调查问题的根源。
答案 5 :(得分:1)
一般来说,您永远不应该编写捕获java.lang.Error
或其任何子类(包括OutOfMemoryError
)的catch块。唯一的例外是,如果您使用第三方库,当它们应该具有子类Error
时会抛出RuntimeException
的自定义子类。这实际上只是解决了代码中的错误。
来自java.lang.Error
的{{3}}:
错误是Throwable的子类,表示严重问题 合理的申请不应该试图抓住。
如果您的应用程序出现问题,即使其中一个线程因为OOME而死亡,您仍然可以继续运行。
首先,您可能需要检查是否可以将剩余的线程标记为守护程序线程。如果只有守护程序线程保留在JVM中,它将运行所有关闭挂钩并尽可能有序地终止。为此,您需要在线程对象启动之前调用setDaemon(true)
。如果线程实际上是由框架或其他代码创建的,则可能必须使用不同的方法来设置该标志。
另一个选项是为有问题的线程分配一个未捕获的异常处理程序,并调用System.exit()
或绝对必要的Runtime.getRuntime().halt()
。调用暂停是非常危险的,因为关闭挂钩甚至不会尝试运行,但在某些情况下,如果已经抛出OOME,则停止可能会在System.exit失败的情况下工作。
答案 6 :(得分:1)
我建议在应用程序中处理所有未捕获的异常,以确保它在终止之前尝试为您提供最佳数据。 然后有一个外部脚本,当它崩溃时重启你的进程。
public class ExitProcessOnUncaughtException implements UncaughtExceptionHandler
{
static public void register()
{
Thread.setDefaultUncaughtExceptionHandler(new ExitProcessOnUncaughtException());
}
private ExitProcessOnUncaughtException() {}
@Override
public void uncaughtException(Thread t, Throwable e)
{
try {
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
System.out.println("Uncaught exception caught"+ " in thread: "+t);
System.out.flush();
System.out.println();
System.err.println(writer.getBuffer().toString());
System.err.flush();
printFullCoreDump();
} finally {
Runtime.getRuntime().halt(1);
}
}
public static void printFullCoreDump()
{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("\n"+
sdf.format(System.currentTimeMillis())+"\n"+
"All Stack Trace:\n"+
getAllStackTraces()+
"\nHeap\n"+
getHeapInfo()+
"\n");
}
public static String getAllStackTraces()
{
String ret="";
Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
for (Entry<Thread, StackTraceElement[]> entry : allStackTraces.entrySet())
ret+=getThreadInfo(entry.getKey(),entry.getValue())+"\n";
return ret;
}
public static String getHeapInfo()
{
String ret="";
List<MemoryPoolMXBean> memBeans = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean mpool : memBeans) {
MemoryUsage usage = mpool.getUsage();
String name = mpool.getName();
long used = usage.getUsed();
long max = usage.getMax();
int pctUsed = (int) (used * 100 / max);
ret+=" "+name+" total: "+(max/1000)+"K, "+pctUsed+"% used\n";
}
return ret;
}
public static String getThreadInfo(Thread thread, StackTraceElement[] stack)
{
String ret="";
ret+="\n\""+thread.getName()+"\"";
if (thread.isDaemon())
ret+=" daemon";
ret+=
" prio="+thread.getPriority()+
" tid="+String.format("0x%08x", thread.getId());
if (stack.length>0)
ret+=" in "+stack[0].getClassName()+"."+stack[0].getMethodName()+"()";
ret+="\n java.lang.Thread.State: "+thread.getState()+"\n";
ret+=getStackTrace(stack);
return ret;
}
public static String getStackTrace(StackTraceElement[] stack)
{
String ret="";
for (StackTraceElement element : stack)
ret+="\tat "+element+"\n";
return ret;
}
}
答案 7 :(得分:0)
您可以使用针对OOME的try catch包围线程代码,并在发生此类事件时进行一些手动清理。一个技巧是让你的线程函数只是尝试捕获另一个函数。在内存错误时,它应该释放堆栈上的一些空间,允许您进行一些快速删除。如果你在捕获后立即对某些资源执行垃圾收集请求和/或设置一个死标志以告诉其他线程退出,这应该有效。
一旦与OOME的线程死亡并且你对它的元素进行了一些收集,你应该有足够的可用空间让其他线程以有序的方式退出。这是一个更优雅的戒烟,有机会在死前记录问题。
答案 8 :(得分:0)
由于JVM选项
-XX:+ExitOnOutOfMemoryError
-XX:+CrashOnOutOfMemoryError
-XX:OnOutOfMemoryError=...
不起作用(请参见the corresponding JDK bug report),可能值得尝试使用工具jkill。如果内存或可用线程已用完,它将通过JVMTI注册并退出VM。
在我的测试中,它按预期方式工作(以及我希望JVM选项如何工作)。
答案 9 :(得分:-2)
您不应该以任何方式处理 OOM。您必须修复它。为此,当然,您必须找到内存泄漏的根本原因:哪些对象泄漏以及原因。不幸的是,正如this blog series中所见,这并不容易。但你可以尝试Plumbr。它应该比其他人更好:)