Рубрики
Без рубрики

Что вызывает java.lang.OutOfMemoryError: не удалось создать новый собственный поток

Узнайте о том, что вызывает java.lang.OutOfMemoryError: не удалось создать новую ошибку собственного потока.

Автор оригинала: baeldung.

1. введение

В этом уроке мы обсудим причину и возможные средства устранения java.lang.OutOfMemoryError: не удалось создать новый собственный поток ошибка.

2. Понимание проблемы

2.1. Причина проблемы

Большинство Java-приложений многопоточны по своей природе , состоят из нескольких компонентов, выполняют определенные задачи и выполняются в разных потоках. Однако базовая операционная система (ОС) накладывает ограничение на максимальное количество потоков , которые может создать приложение Java.

JVM выдает ошибку не удается создать новый собственный поток , когда JVM запрашивает базовую ОС для нового потока, и ОС не способна создавать новые потоки ядра, также известные как потоки ОС или системные потоки . Последовательность событий выглядит следующим образом:

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

2.2. Модель распределения потоков

ОС обычно имеет два типа потоков – пользовательские потоки (потоки, созданные приложением Java) и потоки ядра . Пользовательские потоки поддерживаются над потоками ядра, и потоки ядра управляются ОС.

Между ними есть три общих отношения:

  1. Many-To-One – Множество пользовательских потоков сопоставляются с одним потоком ядра
  2. Один к одному – Сопоставление одного пользовательского потока с одним потоком ядра
  3. Многие ко многим -Многие пользовательские потоки мультиплексируются на меньшее или равное количество потоков ядра

3. Воспроизведение ошибки

Мы можем легко воссоздать эту проблему, создав потоки в непрерывном цикле, а затем заставив потоки ждать:

while (true) {
  new Thread(() -> {
    try {
        TimeUnit.HOURS.sleep(1);     
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
  }).start();
}

Поскольку мы держимся за каждый поток в течение часа, постоянно создавая новые, мы быстро достигнем максимального количества потоков из ОС.

4. Решения

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

Однако это не идеальное решение, поскольку OutOfMemoryError , вероятно, указывает на ошибку программирования. Давайте рассмотрим некоторые другие способы решения этой проблемы.

4.1. Использование Фреймворка Услуг Исполнителя

Использование платформы executorservice Java для администрирования потоков может в определенной степени решить эту проблему. Платформа службы исполнителя по умолчанию или пользовательская конфигурация исполнителя могут управлять созданием потока.

Мы можем использовать метод Executors#newFixedThreadPool , чтобы установить максимальное количество потоков, которые могут использоваться одновременно:

ExecutorService executorService = Executors.newFixedThreadPool(5);

Runnable runnableTask = () -> {
  try {
    TimeUnit.HOURS.sleep(1);
  } catch (InterruptedException e) {
      // Handle Exception
  }
};

IntStream.rangeClosed(1, 10)
  .forEach(i -> executorService.submit(runnableTask));

assertThat(((ThreadPoolExecutor) executorService).getQueue().size(), is(equalTo(5)));

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

Поскольку пул потоков состоит из пяти потоков, он может обрабатывать не более пяти задач в любое время.

4.2. Захват и анализ дампа потока

Захват и анализ дампа потока полезен для понимания состояния потока.

Давайте посмотрим на образец дампа потоков и посмотрим, что мы можем узнать:

Приведенный выше снимок потока взят из Java VisualVM для примера, представленного ранее. Этот снимок наглядно демонстрирует непрерывное создание потока.

Как только мы определим, что существует непрерывное создание потоков, мы сможем захватить дамп потоков приложения, чтобы идентифицировать исходный код, создающий потоки:

В приведенном выше снимке мы можем определить код, ответственный за создание потока. Это дает полезную информацию для принятия соответствующих мер.

5. Заключение

В этой статье мы узнали о java.lang.OutOfMemoryError: не удалось создать новый собственный поток ошибка, и мы видели, что это вызвано чрезмерным созданием потока в приложении Java.

Мы рассмотрели некоторые решения для устранения и анализа ошибки, рассмотрев фреймворк ExecutorService и анализ дампа потока как две полезные меры для решения этой проблемы.

Как всегда, исходный код статьи доступен на GitHub .