Java 可运行内存不足

Java Runnable Out of Memory

抛开不良做法...

我们实现了一个 Java 线程 Runnable 并且在它的 run() 中有一个 while(true) 做一些事情。 while 里面的第一件事是 try/catch/finally 说,如果有任何东西被抓到,就睡觉,然后 "exit" (这会继续一段时间)。

如果更高级别的线程外部存在 OutOfMemory 问题,线程会完全死掉吗?如果是这样,try/catch/finally 不会 catch 因为 OOM 的执行超出了该范围?

线程是否会被垃圾回收(它不优雅地死掉了)。

TLDR:如果 Runnable 得到 OutOfMemory 它会死掉并停在它的轨道上,还是在执行范围内抛出异常?

run(){
  while(true){
    try {
      stuff
    } catch{  //blah
    } finally{sleep}
  }
}

作业时间又到了?

OutOfMemoryError 是错误,不是异常。您可以捕捉到 Error - 毕竟,它们源自 ThrowableException 也是如此 - 但这不是一个好主意。

您的线程不会死,但您的整个 JVM 会死。因为确实没有从 OutOfMemoryError.

中恢复的优雅方法

当您在 while 循环中捕获 OutOfMemoryError 时,您分配的所有内容(假设它们仅在您的循环中被引用)直到内存错误点,将有资格进行垃圾收集。(这也将是没有错误的情况)

如果一个线程因为 OutOfMemoryError 而死亡,它的堆栈将被销毁,所有仅从该堆栈引用的对象也将有资格进行垃圾回收。

即使主线程因为 OutOfMemoryError 而死,如果你有非守护线程,jvm 也不会死。

OutOfMemoryError 可能有不同的原因,它们在此处列出 https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks002.html

一般建议抓到OutOfMemoryError就清理退出。

Error 而不是 Exception 发生时,整个 JVM -> 在大多数情况下所有线程都会死掉。
如果你的后台线程不使用共享资源,它可能幸存下来,但正如所说,它非常罕见。 啊,如果它是一个恶魔威胁,它会立即死亡。
try catch final 是什么意思? 您无法真正捕捉到错误,它就像一颗子弹:

an error is catchable, you can try to catch but if its thrown you die.

分配失败时抛出OutOfMemoryError。因此,它会向上传递到进行分配尝试的线程的调用链。只有 try … catch 块封装了这个调用链,resp。分配本身可能会捕获它。

对 JVM 的整体操作和其他线程没有直接影响。当然,如果抛出 OutOfMemoryError 的原因是 JVM 内存不足,那么这种异常情况本身就有可能影响整个 JVM 操作或同时影响其他线程。例如,所有 个线程此时尝试分配可能会失败并出现错误。

this answer 所示,如果内存太低以至于无法构造单独的错误实例,多个线程可能会遇到相同的 OutOfMemoryError 实例。但这并不意味着抛出的错误可能会转移到不同的线程。线程中的失败分配与该线程中捕获的错误之间仍然存在因果关系。或者,如果异步操作失败,并发框架可能会将错误实例移交给启动线程。

JVM 在 OutOfMemoryError 时自动退出的唯一原因是间接原因;如果所有非守护线程响应 OutOfMemoryError 而退出,例如当根本没有捕获它时,JVM 将退出,因为这是没有非守护线程处于活动状态时的标准行为。

在某些情况下,当 JVM 的内存通常不低时,应用程序可以从单个失败的分配中恢复,例如如果失败的分配非常大(表达式 new long[Integer.MAX_VALUE-8] 需要 ~16GiB),如果它达到一般限制(HotSpot 不支持 new long[Integer.MAX_VALUE],即使有足够的内存),或者需要特殊内存而不是普通堆 space(本地缓冲区、图形内存或旧 JVM 中的 PermGen space)。