java — Как остановить утечки обработки для многих одновременных потоков

#java #multithreading

#java #многопоточность

Вопрос:

Я столкнулся с очевидной утечкой дескрипторов, но я надеюсь, что есть обходной путь или что я просто неправильно понимаю, как JRE обрабатывает потоки внутри.

Пожалуйста, обратите внимание, что ответ на этот вопрос НЕ «используйте пулы потоков и ограничьте максимальное количество одновременных потоков». Это не решает основную проблему или возможные решения, особенно если желательно очень большое количество максимально одновременных потоков.

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

Количество дескрипторов из Process Explorer приведено ниже для:

Пик — Максимальное количество дескрипторов после создания и запуска 600 потоков

Завершенные потоки — Количество дескрипторов после завершения всех 600 потоков (просто оставляя основной поток и потоки JVM)

            Handle Counts
JRE    Peak    Threads Ended
1.4    2632    232
5      3859    259
6      4311    3111
8      4321    3121
  

Здесь два основных замечания:

  1. JRE 5 и более поздние версии имеют значительно более высокие издержки обработки потоков, чем 1.4
  2. JRE 6 и более поздние версии не освобождают дескрипторы после завершения потоков

Многократное создание и запуск одного и того же или меньшего количества потоков не приводит к увеличению количества дескрипторов с пиковым / конечным дескриптором (за исключением использования JRE 1.5, в котором может произойти другая более медленная утечка). Увеличение максимального количества одновременных потоков увеличивает оба числа.

Все JRE, используемые для тестирования, относятся к разновидности Sun / Oracle.

GC никогда не приходит и не устраняет беспорядок, основанный на нескольких днях тестирования и наблюдения за проблемой.

Кто-нибудь знает, как гарантировать, что ресурсы дескриптора для потоков освобождаются после использования в Java 6 и более поздних версиях без перезапуска JVM?

Ниже приведен тестовый код для воспроизведения проблемы:

 public class ThreadLeakTest 
{
    public static void main(String[] args) 
    {
        new ThreadLeakTest();
    }

    public ThreadLeakTest()
    {
        while (true)
        {
            for (int i = 0; i < 600;   i)
            {
                LeakTestThread testThread = new LeakTestThread();
                Thread t = new Thread(testThread);
                t.start();
            }

            sleepSafe(20000);
        }
    }

    private class LeakTestThread implements Runnable
    {
        public void run()
        {
            sleepSafe(15000);
        }
    }

    private static final void sleepSafe(int sleepTime)
    {
        try
        {   
            Thread.sleep(sleepTime);
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
    }
}
  

Комментарии:

1. @GhostCat Ваш ответ подтверждает, что известно о существовании проблемы или одной очень похожей, и что официального решения пока не существует. Осведомленность других людей о проблеме не является решением проблемы. Я уже дал понять в ответ на ваш ответ, что предложения по ограничению целевой ОС и / или изменению языка программирования также не являются приемлемыми решениями. Другие пользователи могут прийти в любое время в будущем с ответами, которые решают проблему.

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

Ответ №1:

Похоже, это известная ошибка, 8154063:

Виртуальная машина поддерживает пул объектов PlatformEvent (которые сопоставляются с событиями в Windows). Этот пул увеличится до максимума, необходимого для максимального количества одновременных потоков, существующих в виртуальной машине одновременно. Обычно нам не хватает памяти для создания потоков задолго до того, как у нас закончатся дескрипторы. Таким образом, во время первого ожидания существует только основной поток и несколько потоков виртуальной машины, а общее количество дескрипторов невелико.

Затем мы запускаем 3000 потоков, каждый из которых использует как минимум 5 дескрипторов (и, основываясь на сообщаемых выходных данных, я предполагаю, что где-то в реализации скрывается 6-й дескриптор). Тогда в зависимости от размера машины и планирования будет некоторое различие в количестве одновременных потоков, но предположим, что всего 3000, и легко увидеть, откуда берутся максимальные значения.

Таким образом, здесь, похоже, нет хорошего решения для Windows, кроме уменьшения количества потоков. Помимо этого, более общая мысль: кажется, что вы используете свое оборудование для «реальных» серверных задач. Если это так, рассмотрите:

  • использование ОС, которая тогда лучше работала с Java (есть причина, по которой в наши дни так много крупных серверов работают под управлением Linux)
  • если вам нужно остаться с Windows, вы могли бы либо рассмотреть возможность использования платформы, которая «ближе» к ней, например .NET и C #. Или вы выбираете платформу JVM, такую как Akka, которая обеспечивает свой «параллелизм» без большого количества потоков.

Комментарии:

1. Спасибо за ссылку на сообщение об ошибке. На этой странице (8154063) говорится, что отчет об ошибке является дубликатом 8145999. Однако 8145999, похоже, скрыт от общего просмотра, поэтому статус неизвестен. проблема в 8u202 все еще существует более двух лет спустя, так что это не обнадеживает.

2. Я тоже это видел. Вот почему я перешел к этому.

3. Что касается ваших правок: требуется кроссплатформенность, поэтому я не могу склоняться к Windows или Linux. Akka не выглядит жизнеспособным вариантом для моих целей. Надеюсь, ваши предложения помогут другим, кто столкнется с этой страницей.