Java `OutOfMemoryError` при создании <100 потоков

Я читал и тестировал и стучал головой о стену в течение дня из-за этой ошибки.

У меня есть Java-код в классе с именем Listener, который выглядит как

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 ГБ оперативной памяти, но также должен работать с базой данных, поддерживающей программу.

После создания 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 думать, что у нее больше нет памяти для новых потоков?

UPDATE: Возможно, это больше, чем я думал - мне удалось получить эту ошибку (только один раз), когда я использовал ^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, заставляющий вмешиваться в другой, но я не могу себе представить, что, если у меня все еще есть свободная память и я вообще не использую swap? Сервер -Xmx1G -Xss104k Клиент -Xmx10M

UPDATE2: Отказавшись от библиотеки perl Forks::Super и запуская клиентов из bash, я могу получить до 34 потоков до того, как сервер разбился с OOME, поэтому запуск нескольких клиентов определенно повлиял на сервер, но в то же время я все равно должен быть способен запускать более 34 (68, если подсчитывать клиентов) java-потоков за раз. Какие системные ресурсы блокируют создание большего количества потоков (т.е. Где я должен искать свиней)? Когда все (клиенты, сервер, GC...) в то же время исчерпывает память, top говорит об использовании моего процессора и памяти:

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

UPDATE3: Описан ли в hs_error log ниже, что моя 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

Ответ 1

Вы можете ограничить на max user processes, чтобы узнать, как использовать лимит:

ulimit -u

Чтобы изменить предел:

В /etc/security/limits.conf установите:

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

Возможно, вам придется добавить какую-то другую конфигурацию, если этого недостаточно, чтобы увидеть ссылку .

Примечание. OP обнаружил этот отчет об ошибках в fedora и centos, который объясняет ограничения редактирования /etc/security/limits.conf.

Ответ 2

Вероятно, ваша проблема связана с тем, что JVM не может выделить стек для новых потоков. По иронии судьбы, эта проблема может быть решена путем уменьшения пространства кучи (-Xmx) и пространства стека (-Xss). Проверьте здесь, например, за хорошее объяснение: http://www.blogsoncloud.com/jsp/techSols/java-lang-OutOfMemoryError-unable-to-create-new-native-thread.jsp

Ответ 3

Он не пропускает память для ваших новых потоков, она пропускает фактические потоки. Система, вероятно, остановит вас: существует ограничение на количество потоков, которые пользователь может создать. Вы можете запросить его таким образом:

cat /proc/sys/kernel/threads-max

Обратите внимание, что на другие процессы на одном компьютере могут воздействовать другие процессы, вы также создаете много потоков. Вы можете найти этот вопрос полезным: Максимальное количество потоков в процессе в Linux?

Ответ 4

Только для уточнения:

Вы предоставляете ServerSocket для Thread. Вы отправляете данные в этот сокет? Возможно, вы храните много данных в контексте Thread-Context. Посмотрите на шаблон, в котором вы храните Streamdata в byte[].