«Java.lang.OutOfMemoryError: невозможно создать новый собственный поток»


124

Мы получаем "java.lang.OutOfMemoryError : unable to create new native Thread"виртуальную машину 8 ГБ RAM после 32k потоков (ps -eLF | grep -c java)

Однако "top" and "free -m" shows 50% free memory available. JDk - 64-битный и пробовал как с HotSpot, так и с JRockit. Сервер имеет Linux 2.6.18

Мы также пробовали OS stack size (ulimit -s)настраивать и ограничивать максимальный процесс (ulimit -u), увеличивать limit.conf, но все тщетно.

Также мы испробовали почти все возможные комбинации размеров кучи, оставив их низким, высоким и т. Д.

Скрипт, который мы используем для запуска приложения,

/opt/jrockit-jdk1.6/bin/java -Xms512m -Xmx512m -Xss128k -jar JavaNatSimulator.jar /opt/tools/jnatclients/natSimulator.properties

Спасибо за ответ.

Мы пробовали редактировать /etc/security/limits.conf и ulimit, но все равно

[root@jboss02 ~]# 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) 72192
max locked memory       (kbytes, -l) 32
max memory size         (kbytes, -m) unlimited
open files                      (-n) 65535
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) 72192
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

11
В операционных системах есть ограничения на количество создаваемых потоков. Почему вы создаете более 32 тысяч потоков? В вашей системе, скорее всего, нет тысяч процессорных ядер, поэтому создание такого количества потоков бесполезно. ExecutorServiceВместо этого используйте пул потоков ( ).
Джеспер

Спасибо за ответ. Мы используем библиотеку с открытым исходным кодом и пытаемся протестировать ее. Любая библиотека с открытым исходным кодом создает так много потоков. Но чего я не понимаю, это когда "верх" показывает 50% свободной памяти, тогда почему ошибка OutOfMemory.
Дипак Тевани

Библиотека с открытым исходным кодом, которую мы используем в ICE4j Library
Дипак Тевани

11
OutOfMemoryError не обязательно означает, что пространство кучи или «общая» оперативная память исчерпаны. В этом случае ясно, что сбой произошел из-за того, что ОС не имела ресурсов для выделения дополнительного потока. Наличие 50% свободной памяти не имеет отношения к этой конкретной ошибке.
Анджей Дойл

1
Какие еще ресурсы требуются для создания новых потоков. У нас создалось впечатление, что если мы увеличим объем оперативной памяти, то сможем создать больше потоков. Пожалуйста,
направьте

Ответы:


80

Это не проблема памяти, хотя название исключения настоятельно предполагает это, а проблема с ресурсами операционной системы. У вас заканчиваются собственные потоки, т.е. сколько потоков операционная система позволит использовать вашей JVM.

Это редкая проблема, потому что вам редко нужно такое количество. У вас много безусловных потоков, порождаемых там, где потоки должны, но не завершаться?

Вы можете подумать о том, чтобы переписать использование Callable / Runnables под управлением Executor, если это вообще возможно. Существует множество стандартных исполнителей с различным поведением, которым ваш код может легко управлять.

(Есть много причин, по которым количество потоков ограничено, но они различаются от операционной системы к операционной системе)


Спасибо за ответ. Мы используем библиотеку с открытым исходным кодом ICE4j и пытаемся протестировать ее. Мы не можем увеличить лимит потоков в ОС, если знаем, что на сервере осталось 50% памяти.
Дипак Тевани

Возможно, но я думаю, что это вам как таковое не поможет. Если во время нагрузочного тестирования у вас заканчиваются ресурсы, вам нужно иметь возможность контролировать, что происходит в вашем приложении. Почему у вас одновременно активны 32000 потоков?
Торбьёрн Равн Андерсен

Мы создаем 11K клиентов, которые используют 32K потоков для чтения, записи данных в сокеты UDP. Из этих 32 тыс.
Потоков 10 тыс

Я считаю, что эта проблема решена на современных веб-серверах. Также udp может терять пакеты - по какой причине вы не используете только веб-сервер?
Торбьёрн Равн Андерсен

7
Поскольку исключение OutOfMemory должно было называться OutOfResources. Операционная система не может предоставить необходимый вам ресурс. (И оказалось, что я не знал ice4j)
Торбьёрн Равн Андерсен

14

Я столкнулся с той же проблемой во время нагрузочного теста, причина в том, что JVM не может создать новый поток Java дальше. Ниже приведен исходный код JVM

if (native_thread->osthread() == NULL) {    
// No one should hold a reference to the 'native_thread'.    
    delete native_thread;   
if (JvmtiExport::should_post_resource_exhausted()) {      
    JvmtiExport::post_resource_exhausted(        
        JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | 
        JVMTI_RESOURCE_EXHAUSTED_THREADS, 
        "unable to create new native thread");    
    } THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), "unable to create new native thread");  
} Thread::start(native_thread);`

Основная причина: JVM выдает это исключение, когда JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR (ресурсы исчерпаны (означает, что память исчерпана)) или JVMTI_RESOURCE_EXHAUSTED_THREADS (потоки исчерпаны).

В моем случае Jboss создает слишком много потоков для обслуживания запроса, но все потоки заблокированы. Из-за этого JVM исчерпывается потоками, а также памятью (каждый поток хранит память, которая не освобождается, потому что каждый поток заблокирован).

Проанализировав дампы потоков Java, мы обнаружили, что около 61 тыс. Потоков заблокировано одним из наших методов, что и вызывает эту проблему. Ниже представлена ​​часть дампа потока

"SimpleAsyncTaskExecutor-16562" #38070 prio=5 os_prio=0 tid=0x00007f9985440000 nid=0x2ca6 waiting for monitor entry [0x00007f9d58c2d000]
   java.lang.Thread.State: BLOCKED (on object monitor)

Как проходила блокировка метода? Никогда не вернусь?
Торбьёрн Равн Андерсен

8

Вероятно, ваша ОС не позволяет количество потоков, которые вы пытаетесь создать, или вы достигли некоторого ограничения в JVM. Особенно, если это такое круглое число, как 32к, очень вероятно, что виноват тот или иной лимит.

Вы уверены, что вам действительно нужно 32k потоков? Большинство современных языков имеют некоторую поддержку для пулов многоразовых потоков - я уверен, что в Java тоже что-то есть (например ExecutorService, как сказал пользователь Джеспер). Возможно, вы могли бы запросить потоки из такого пула вместо того, чтобы вручную создавать новые.


1
Спасибо за ответ. Мы используем библиотеку с открытым исходным кодом ICE4j и пытаемся ее протестировать. Мы не можем увеличить лимит потоков в ОС, если знаем, что на сервере осталось 50% памяти.
Дипак Тевани

1
Мы создаем 11K клиентов, которые используют 32K потоков для чтения, записи данных в сокеты UDP. Из этих 32 тыс.
Потоков 10 тыс

7

Я бы порекомендовал также взглянуть на размер стека потоков и посмотреть, сможете ли вы создать больше потоков. Размер стека потоков по умолчанию для JRockit 1.5 / 1.6 составляет 1 МБ для 64-разрядной виртуальной машины в ОС Linux. 32K потоков потребует значительного количества физической и виртуальной памяти для выполнения этого требования.

Попробуйте уменьшить размер стека до 512 КБ в качестве отправной точки и посмотрите, поможет ли это создать больше потоков для вашего приложения. Я также рекомендую изучить горизонтальное масштабирование, например, разделение обработки вашего приложения на большее количество физических или виртуальных машин.

При использовании 64-разрядной виртуальной машины истинный предел будет зависеть от доступности физической и виртуальной памяти ОС и параметров настройки ОС, таких как ulimitc. Я также рекомендую следующую статью в качестве ссылки:

OutOfMemoryError: невозможно создать новый собственный поток - проблема раскрыта


6

Если jvm запускается через systemd, в некоторых ОС Linux может быть ограничение maxTasks на процесс (задачи фактически означают потоки).

Вы можете проверить это, запустив «статус службы» и проверив, есть ли ограничение maxTasks. Если есть, вы можете удалить его, отредактировав /etc/systemd/system.conf, добавив конфиг: DefaultTasksMax = infinity


Чтобы расширить это, если вы подключитесь по SSH к серверу, зараженному systemd, а затем запустите java-процесс из CLI, вы также можете достичь лимита глупых задач, наложенного на службу sshd .
sayap

3

У меня была такая же проблема из-за призрачных процессов, которые не отображались при использовании top в bash. Это не позволяло JVM создавать больше потоков.

Для меня это разрешилось при перечислении всех java-процессов с jps (просто выполните jpsв своей оболочке) и уничтожении их отдельно с помощью команды kill -9 pidbash для каждого призрачного процесса.

Это может помочь в некоторых сценариях.


2

У вас есть шанс столкнуться с java.lang.OutOfMemoryError: Unable to create new native threadпроблемой всякий раз, когда JVM запрашивает у ОС новый поток. Всякий раз, когда базовая ОС не может выделить новый собственный поток, будет выброшена эта ошибка OutOfMemoryError. Точный предел для собственных потоков очень зависит от платформы, поэтому рекомендуется выяснить эти ограничения, выполнив тест, аналогичный приведенному ниже примеру ссылки. Но в целом возникновение ситуации java.lang.OutOfMemoryError: Unable to create new native threadпроходит следующие фазы:

  1. Новый поток Java запрашивается приложением, работающим внутри JVM.
  2. Собственный код JVM передает запрос на создание нового собственного потока в ОС. ОС пытается создать новый собственный поток, который требует выделения памяти для потока.
  3. ОС откажется от распределения собственной памяти либо потому, что размер 32-битного процесса Java исчерпал свое адресное пространство памяти - например, был достигнут предел размера процесса (2-4) ГБ, - либо виртуальная память ОС была полностью исчерпана.
  4. Выдается ошибка java.lang.OutOfMemoryError: невозможно создать новый собственный поток.

Ссылка: https://plumbr.eu/outofmemoryerror/unable-to-create-new-native-thread


2

Чтобы узнать, какие процессы создают потоки, попробуйте:

ps huH

Обычно я перенаправляю вывод в файл и анализирую файл в автономном режиме (количество потоков для каждого процесса соответствует ожиданиям или нет)


1

Если ваша работа не выполняется из-за OutOfMemmory на узлах, вы можете настроить количество максимальных карт и редукторов, и JVM выберет для каждого. mapred.child.java.opts (значение по умолчанию - 200Xmx) обычно необходимо увеличивать в зависимости от оборудования ваших узлов данных.

Эта ссылка может быть полезной ... пожалуйста, проверьте


1
Мы все уже пробовали то изменение, которое дается по этой ссылке. Но результат тот же :(
Дипак Тевани

1

у вашей конфигурации JBoss есть некоторые проблемы, /opt/jrockit-jdk1.6/bin/java -Xms512m -Xmx512m Xms и Xmx ограничивают использование памяти JBoss до настроенного значения, поэтому из 8 ГБ у вас сервер использует только 512 МБ + немного лишнего для его собственных целей, увеличьте это число, не забудьте оставить немного свободного для ОС и других вещей, работающих там, и, возможно, вы запустите его, несмотря на сомнительный код. Было бы неплохо исправить код, если сможете.


1

Эта ошибка может появиться по следующим двум причинам:

  • В памяти нет места для размещения новых потоков.

  • Количество потоков превышает предел операционной системы.

Я сомневаюсь, что количество потоков превысило лимит для java-процесса

Так что, возможно, проблема в памяти.

потоки не создаются в куче JVM. Они создаются вне кучи JVM. Таким образом, если в ОЗУ осталось меньше места, после выделения кучи JVM приложение выполнит ошибку «java.lang.OutOfMemoryError: невозможно создать новый собственный поток».

Возможное решение - уменьшить размер кучи или увеличить общий размер оперативной памяти.


0

У меня была такая же проблема, и оказалось, что это неправильное использование Java API. Я инициализировал компоновщик в методе пакетной обработки, который не должен был инициализироваться более одного раза.

В основном я делал что-то вроде:

for (batch in batches) {
    process_batch(batch)
}

def process_batch(batch) {
    var client = TransportClient.builder().build()
    client.processList(batch)
}

когда я должен был это сделать:

for (batch in batches) {
    var client = TransportClient.builder().build()
    process_batch(batch, client)
}

def process_batch(batch, client) {
    client.processList(batch)
}

-4

Прежде всего, я бы не стал винить в этом ОС / ВМ ... скорее разработчика, написавшего код, который создает ооочень много потоков . По сути, где-то в вашем коде (или у стороннего разработчика) множество потоков создается бесконтрольно .

Внимательно просмотрите трассировку стека / код и контролируйте количество создаваемых потоков. Обычно вашему приложению не требуется большое количество потоков, иначе это уже другая проблема.


10
Это не решение вопроса.
ftrujillo
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.