我们最近遇到了内存不足的情况,原因是我们的 Tomcat 服务器上“无法创建新的 native 线程”。我们知道问题是什么 - 有一个错误产生了太多线程,超出了 ulimit 的范围。在允许的盒子上。
令我感兴趣的是,当这种情况发生时,JVM 并没有转储堆。我检查了 Java 6 和 8 中的行为是否一致。
我编写了一些快速的 Groovy PoC 来证明这种行为。因此,虽然我在这里使用了 groovy,但其行为与我的 Servlet 应用程序一致(只是让我更容易分享问题的工作模型)。
转储堆 - TraditionalOOM.groovy
:
byte[] b = new byte[1000000000]
是否不转储堆 - ManyThreads.groovy
:
int MAX = 5000;
for (int i = 0; i < MAX; i++) {
new Thread() {
public void run() {
sleep(1000);
}
}.start();
}
Thread.activeCount();
与此问题相关的 Java 选项(恕我直言)如下 2 个:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/somewhere
我想知道:
- 为什么 JVM 在 OOM 期间不转储堆(即使转储线程转储也会有帮助)?
- 答案是显而易见的吗:JVM 没有线程来执行转储?
更新
我得到了答案 - 似乎这是预期的行为,而 Oracle(或 Sun)已将这种行为视为缺陷而拒绝 - http://bugs.java.com/bugdatabase/view_bug.do;jsessionid=74ee28dae329645479a51f6c78f2?bug_id=6784422
最佳答案
我得到了答案 - 似乎这是预期的行为,而 Oracle(或 Sun)已将这种行为视为缺陷而拒绝 -
http://bugs.java.com/bugdatabase/view_bug.do;jsessionid=74ee28dae329645479a51f6c78f2?bug_id=6784422
该链接指出“ native 线程的分配不是来自堆或永久代 - 因此分配失败不会导致堆转储。(转储堆没有什么意义,因为这不是耗尽的内容。)”
我并不完全同意这一点,因为堆转储还包含大量有关线程的信息。如果这是 Oracle(或 Sun)的立场,JVM 至少应该转储线程转储。
关于java - 由于 "unable to create new native thread"导致 OutOfMemory 时的堆转储,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34145797/