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
- 毕竟,它们源自 Throwable
,Exception
也是如此 - 但这不是一个好主意。
您的线程不会死,但您的整个 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)。
抛开不良做法...
我们实现了一个 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
- 毕竟,它们源自 Throwable
,Exception
也是如此 - 但这不是一个好主意。
您的线程不会死,但您的整个 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)。