即使有足够的空闲内存,仍然出现OutOfMemoryError错误

4

即使我仍有足够的空闲RAM,我仍会遇到“java.lang.OutOfMemoryError”错误。我拍摄的内存转储文件大小在200MB和1GB之间,而我的服务器有24GB的RAM。我设置了“-Xmx12288m -Xms12288m”。

另外,当我尝试登录服务器时,我经常遇到以下错误:

-bash: fork: retry: Resource temporarily unavailable
-bash: fork: retry: Resource temporarily unavailable
-bash: fork: retry: Resource temporarily unavailable
-bash: fork: retry: Resource temporarily unavailable
-bash: fork: Resource temporarily unavailable

我将其缩小到以下代码片段:

我将其缩小到以下代码片段:

import org.snmp4j.Snmp;
import org.snmp4j.transport.DefaultUdpTransportMapping;

    long n = 0;
    while (true) {
        DefaultUdpTransportMapping transport = null;
        try {
            transport = new DefaultUdpTransportMapping();
            transport.listen();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
//      } finally {             // (*) I forgot this
//          transport.close();  // (*) I forgot this
        }

        n++;
        double freeMemMB = Runtime.getRuntime().freeMemory() / 1024 / 1024;
        System.out.println("Created " + n
                + " DefaultUdpTransportMappings. Free Mem (mb): "
                + freeMemMB);
    }

在我的开发机上执行 mvn exec:java 命令,输出如下:
Created 2026 DefaultUdpTransportMappings. Free Mem (mb): 299.0
Created 2027 DefaultUdpTransportMappings. Free Mem (mb): 299.0
[WARNING] 
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:293)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Thread.java:714)
    at org.snmp4j.util.DefaultThreadFactory$WorkerThread.run(DefaultThreadFactory.java:91)
    at org.snmp4j.transport.DefaultUdpTransportMapping.listen(DefaultUdpTransportMapping.java:168)
    at App.main(App.java:19)
    ... 6 more

我发现出现错误是因为没有关闭DefaultUdpTransportMapping。启用finally { ... }块可以解决问题。现在我想知道我达到了哪些限制(如果不是免费内存的数量)。服务器上的ulimits如下:

core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 191968
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 10240
cpu time               (seconds, -t) unlimited
max user processes              (-u) 1024
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

在我的开发者Mac电脑上:

-t: cpu time (seconds)              unlimited
-f: file size (blocks)              unlimited
-d: data seg size (kbytes)          unlimited
-s: stack size (kbytes)             8192
-c: core file size (blocks)         0
-v: address space (kbytes)          unlimited
-l: locked-in-memory size (kbytes)  unlimited
-u: processes                       709
-n: file descriptors                2560

我达到了哪个限制?

3
你的-Xmx设置是多少? - Oliver Charlesworth
2
重新运行并附加jconsole(随JDK一起提供)到正在运行的进程;如果您将可视化GC插件添加到其中,则它将向您显示每个堆区域中的大小和分配量。 - Chris K
2
可能是重复的问题:Java:无法创建新的本机线程 - Erich Kitzmueller
ammoQ:链接提到我达到了打开文件描述符的限制。我尝试使用这里的代码https://dev59.com/W2Qo5IYBdhLWcg3wDbwJ#16361505打印打开FDs的数量。当我有2098个打开的FDs时,我会收到`OutOfMemoryError`错误。打开FDs的ulimit为2560。 - Benedikt Köppel
1
@BenediktKöppel 你还应该阅读链接的博客文章http://blog.egilh.com/2006/06/2811aspx.html。 - Erich Kitzmueller
显示剩余2条评论
1个回答

2

java.lang.OutOfMemoryError: unable to create new native thread是一个令人困惑的消息,因为它实际上与堆内存耗尽无关。因此,堆大小设置(Xmx和Xms)对此情况没有影响。

当新的操作进程无法为您的应用程序创建时,会抛出异常,这可能是因为达到了最大进程/打开文件句柄数或系统上没有剩余内存来创建新线程。

关于ulimit设置,它可以是文件描述符数量、堆栈大小或进程数量。堆栈大小是每个线程的数量,线程数乘以堆栈大小将是已使用内存的数量。

通常,像您的情况一样,遇到此异常意味着您的应用程序未正确关闭其线程并保持系统进程。这就是为什么关闭传输会为您解决问题的原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接