创建小于 100 个线程时的 Java `OutOfMemoryError`

标签 java jvm threadpool out-of-memory openjdk

由于这个错误,我已经阅读、测试并用头撞墙一天多了。

我在名为 Listener 的类中有一些 Java 代码,看起来像这样

ExecutorService executor = Executors.newFixedThreadPool(NTHREADS);
boolean listening = true;
int count = 0;
while (listening) {
    Runnable worker;
    try {
        worker = new ServerThread(serverSocket.accept()); // this is line 254
        executor.execute(worker);
        count++;
        logger.info("{} threads started", count);
    } catch (Exception e1){
        //...
    }
}

我一直在调整 JVM 设置 -Xmx(从 1 到 15G 的任何地方)和 -Xss(从 104k 到 512M 的任何地方)。服务器有 24 GB 的 RAM,但还必须运行支持该程序的数据库。

创建 2-20 个线程后(程序中其他地方也存在几十个),我得到错误

Exception in thread "Thread-0" java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:657)
at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:943)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1325)
at xxx.Listener.run(Listener.java:254)

$java -version 产生:

java version "1.6.0_24"
OpenJDK Runtime Environment (IcedTea6 1.11.1) (fedora-65.1.11.1.fc16-x86_64)
OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode)

发生这种情况时系统上总是有大量空闲内存,其他程序继续正常执行。是什么导致 Java 认为它没有更多的内存用于新线程?

更新: 也许这比我想象的要严重——我在使用 ^C 时设法得到了这个错误(只有一次):

OpenJDK 64-Bit Server VM warning: Exception java.lang.OutOfMemoryError occurred dispatching signal SIGINT to handler- the VM may need to be forcibly terminated

当我试图杀死客户端时也发生了同样的情况(也是用 Java 编写并在同一台服务器上运行,它是一个读取文件并通过套接字将其发送到服务器的单线程),所以肯定有超出 JVM 的限制导致一个干扰另一个,但我无法想象如果我仍然有可用内存并且根本不使用交换怎么办?服务器 -Xmx1G -Xss104k 客户端 -Xmx10M

更新 2: 放弃 perl Forks::Super 库并从 bash 运行客户端让我在服务器因 OOME 崩溃之前获得多达 34 个线程,因此运行多个客户端肯定会对服务器产生影响,但在同时,我应该仍然能够一次运行超过 34 个(如果算上客户端,则为 68 个)java 线程。哪些系统资源阻止了更多线程的创建(即我应该去哪里寻找 hog)?当一切(客户端、服务器、GC...)同时耗尽内存时,top 会显示我的 CPU 和内存使用情况:

Cpu(s):  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:  24681040k total,  1029420k used, 23651620k free,    30648k buffers
Swap: 26836988k total,        0k used, 26836988k free,   453620k cached

更新 3: 下面的 hs_error 日志是否表明我的 java 不是 64 位?

# There is insufficient memory for the Java Runtime Environment to continue.
# Cannot create GC thread. Out of system resources.
# Possible reasons:
#   The system is out of physical RAM or swap space
#   In 32 bit mode, the process size limit was hit
# Possible solutions:
#   Reduce memory load on the system
#   Increase physical memory or swap space
#   Check if swap backing store is full
#   Use 64 bit Java on a 64 bit OS
#   Decrease Java heap size (-Xmx/-Xms)
#   Decrease number of Java threads
#   Decrease Java thread stack sizes (-Xss)
#   Set larger code cache with -XX:ReservedCodeCacheSize=
# This output file may be truncated or incomplete.
#
# JRE version: 6.0_24-b24
# Java VM: OpenJDK 64-Bit Server VM (20.0-b12 mixed mode linux-amd64 compressed oops)
# Derivative: IcedTea6 1.11.1
# Distribution: Fedora release 16 (Verne), package fedora-65.1.11.1.fc16-x86_64

最佳答案

你可以通过 max user processes 来限制,以了解你的限制使用:

ulimit -u

要更改限制:

/etc/security/limits.conf 中设置:

user soft nproc [your_val] 
user hard nproc [your_val]

如果还不够,您可能需要添加一些其他配置,请参阅 link .

注意:OP 发现了这个 bug report在 fedora 和 centos 中解释了编辑 /etc/security/limits.conf 的限制。

关于创建小于 100 个线程时的 Java `OutOfMemoryError`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10742634/

相关文章:

java - 在另一个类中添加组件时 JPanel 不更新

java - JVM 的任何编译器都使用 "wide"goto 吗?

java - 是否曾在运行中的 JVM 中调用过此方法

multithreading - 线程,QRunnable 和 QThreadPool,我无法详细介绍

c# - Task.WaitAll 方法 (Task[], Int32) 过期时不释放线程

java - 如何设置处理过程中显示的字符串中的最大单词数?

java - 在 Java 中等待

java - 使用 Eclipse 外部化字符串 - 如何获取 XYZ 语言的字符串?

java - 在 64 位 JVM 上运行的 Eclipse 32 位

c++ - 需要维护秩序的多线程作业