Java:无法创建新的本地线程

19

我有一个Java应用程序,由一个Web托管公司托管。每隔几天我的应用程序会崩溃并显示以下错误:

[2011-03-09 15:52:14,501] ERROR http-12021-9 
java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Thread.java:597)

托管公司说这意味着我的应用程序正在泄漏内存,但我使用的工具显示仍然有可用的空闲内存。由于错误总是创建新的本地线程,我的想法是问题出在JVM配置/操作系统资源上。

我应该如何防止这个错误的发生?

7个回答

13

可能是因为您已达到用户打开文件数的限制。

我相信每个进程/线程都会消耗一个或多个文件描述符。

例如,当您的用户达到此限制时,"no" shell 命令将无法工作,因为 shell 命令会分叉出一个进程来执行(您会看到类似“-bash: fork: retry: Resource temporarily unavailable”的错误)。

我遇到了这个问题,并发现只有当前用户无法生成进程... 其他用户没有受到影响。

要解决这个问题,请增加您的 ulimit -n (最大打开文件数)设置... 详细信息如下。

您可以使用命令查看您的用户限制:

ulimit -a

使用以下方法提高您的最大文件限制:

ulimit -n 65536 

这是我现在拥有的内容:

$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 256797
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 75000
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) 100000
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

查看系统的所有显式限制:

cat /etc/security/limits.conf

请注意:我使用的是Oracle Linux 6.3 - 不同的发行版可能会略有不同。

我在Fedora 20上使用Eclipse Luna也遇到了麻烦。我阅读了很多关于不同内存选项的资料,但仍然无法找出问题所在。这让我抓狂。最后我意识到,默认情况下,Fedora上普通用户可以拥有的进程数量是非常有限的。limits.d/90-nproc.conf文件的内容如下:* soft nproc 1000将这个值提高到5000解决了我的“无法创建新的本地线程”问题。 - arpadf

7

5
这可能是应用程序创建了过多线程的问题。正如文章中提到的那样,更多的线程意味着更多的本地空间内存使用量,需要通过调整来解决。这假定应用程序需要合理数量的线程。但如果应用程序生成线程并且从未终止它们(线程泄漏),则无论进行多少调整都无法解决该问题。同时,也可能是操作系统对每个进程的线程数有限制。 - Helter Scelter
有没有一种简单的方法可以从Java代码本身获取与我的进程关联的线程数?由于这是一个Web托管公司的JVM,我几乎无法访问JVM级别的工具。不过,我可以对我的代码进行仪器化处理(这就是我获取JVM内存信息的方式)。 - tvfoodmaps
据我所知,目前没有办法,但是托管公司有支持团队,如果您教他们如何操作,他们可以为您转储线程。 - Vladimir Dyuzhev

2

当您启动进程时,JVM具有有限的堆大小(默认为128MB)。该服务器可能拥有更多内存,但您的JVM没有 - 您已经使用了全部内存。

您可以使用-Xms-Xmx命令行参数进行更改,但我建议先找到内存泄漏 :)


3
如果这只是一个标准的内存泄漏,我不会看到“无法创建新本机线程”的OOM消息。 - tvfoodmaps
请注意,我在使用最通用的术语,因为在Java中没有直接分配内存然后像在C中一样迷失跟踪的方式。你是如何清除启动的这些线程的? - Brian Roach
谢谢,我在使用Arduino时遇到了这个错误,使用Xms100m解决了它! - Ramast

2
这篇文章是给所有通过systemd运行Java应用程序的人(例如自己创建的Tomcat服务)。
我在服务器上通过Tomcat运行我的Java应用程序。为了方便,我还创建了一个systemd服务单元,这样它就可以在服务器启动时启动,并且我也可以通过systemd(或service tomcat restart)控制它。
但是,systemd单元和它们允许的最大任务数(线程)有一些默认值。对于我的机器,它是195个任务。一旦我在服务单元中更改了TasksMax=1024的值,并使用systemctl daemon-reload重新加载它,一切都按预期工作了。
例如,在/etc/systemd/system/tomcat.service中的服务单元文件。
[Unit]
Description=Tomcat9
After=network.target

[Service]
Type=forking
User=tomcat9
Group=tomcat9

TasksMax=1048

Environment=CATALINA_PID=/opt/tomcat/tomcat9.pid
Environment=JAVA_HOME=/usr/lib/jvm/default-java
Environment=CATALINA_HOME=/opt/tomcat
Environment=CATALINA_BASE=/opt/tomcat
Environment="CATALINA_OPTS=-Xms2048m -Xmx28384m"
Environment="JAVA_OPTS=-Dfile.encoding=UTF-8 -Dnet.sf.ehcache.skipUpdateCheck=true -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+UseParNewGC"

ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh

[Install]
WantedBy=multi-user.target

1

你是否进行了任何内存跟踪?打开jconsole观察或记录你的内存消耗情况24小时。如果(平均)持续上升而不下降,则说明你正在耗尽内存,可能没有足够的内存来存储新线程的详细信息。


1

Linux 处理打开文件数量的问题是一个常见的难题。

可以通过以下命令来解决: ulimit -n 65536 (可以自定义数字)


0

看起来好像是线程泄露了。线程被创建后却停留在某个地方。定期转储线程以查看分配的线程数是否增长。在转储中查找任何睡眠/挂起的线程。

kill -QUIT jvm_pid

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