Что может заставить Java продолжать работать после System.exit ()? - PullRequest
20 голосов
/ 11 апреля 2010

У меня есть Java-программа, которая запускается через ProcessBuilder из другой Java-программы. System.exit(0) вызывается из дочерней программы, но для некоторых наших пользователей (в Windows) процесс java.exe, связанный с дочерним процессом, не завершается. Дочерняя программа не имеет перехватчиков завершения работы и не имеет SecurityManager, которая может помешать System.exit() завершить работу виртуальной машины. Я не могу воспроизвести проблему самостоятельно на Linux или Windows Vista. Пока что единственные сообщения о проблеме поступают от двух пользователей Windows XP и одного пользователя Vista, использующих две разные JRE (1.6.0_15 и 1.6.0_18), но они способны воспроизвести проблему каждый раз.

Может кто-нибудь предложить причины, по которым JVM не сможет завершиться после System.exit(), и только на некоторых машинах?

Редактировать 1: Я попросил пользователя установить JDK, чтобы мы могли получить дамп потока с нарушающей ВМ. Пользователь сказал мне, что процесс виртуальной машины исчезает из VisualVM, как только он нажимает на пункт «Выход» в моем меню - но, согласно диспетчеру задач Windows, процесс не завершился, и неважно, как долго пользователь ждет (минуты, часы), он никогда не завершается.

Редактировать 2: Теперь я подтвердил, что Process.waitFor() в родительской программе никогда не возвращается по крайней мере для одного из пользователей, имеющих проблему. Итак, подведем итог: дочерняя виртуальная машина, кажется, мертва (VisualVM даже не видит ее), но родитель все еще видит процесс как живой, как и Windows.

Ответы [ 8 ]

8 голосов
/ 10 мая 2017

Это может произойти, если в вашем коде (или в используемой вами библиотеке) есть обработчик завершения работы или финализатор, который не завершается чисто.

Более энергичный (поэтому должен использоваться только в крайних случаях!) Способ принудительного отключения - запустить:

Runtime.getRuntime().halt(0);
6 голосов
/ 13 сентября 2010

У родительского процесса есть один поток, предназначенный для использования каждого из дочерних STDOUT и STDERR (который передает эти выходные данные в файл журнала).Насколько я вижу, они работают правильно, так как мы видим все выходные данные, которые ожидаем увидеть в журнале

У меня была похожая проблема с моей программой, не исчезающей из задачи mgr, когдая потреблял стандартный вывод / стандартный поток.в моем случае, если я закрыл поток, который слушал перед вызовом system.exit (), тогда javaw.exe зависал.странно, это не было запись в поток ...

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

4 голосов
/ 11 апреля 2010

Вот несколько сценариев ...

В соответствии с определением потока в http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Thread.html

...

Когда виртуальная машина Java запускаетсяобычно существует единственный поток, не являющийся демоном (который обычно вызывает метод с именем main некоторого назначенного класса).Виртуальная машина Java продолжает выполнять потоки, пока не произойдет одно из следующих событий:

1) Был вызван метод выхода класса Runtime, и менеджер безопасности разрешил выполнение операции выхода.2) Все потоки, которые не являются потоками демонов, умерли, либо возвратившись из вызова метода run, либо выдав исключение, которое распространяется за пределы метода run.

Другая возможность, если был вызван метод runFinalizersOnExit,согласно документации в http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html устарело.Этот метод небезопасен.Это может привести к вызову финализаторов для живых объектов, в то время как другие потоки одновременно манипулируют этими объектами, что приводит к ошибочному поведению или тупику.Включить или отключить финализацию при выходе;это означает, что финализаторы всех объектов, которые имеют финализаторы, которые еще не были вызваны автоматически, должны быть запущены до выхода из среды выполнения Java.По умолчанию финализация при выходе отключена.Если есть менеджер безопасности, его метод checkExit сначала вызывается с 0 в качестве аргумента, чтобы убедиться, что выход разрешен.Это может привести к исключению SecurityException.

1 голос
/ 22 июня 2016

Hy, у меня была та же проблема, но для меня было то, что я использовал удаленную отладку (VmArgs: -Xdebug -Xrunjdwp: transport = dt_socket, address =% port%, server = y, suspend = y), когда я отключил этот процесс java.exe, как и ожидалось, завершился.

1 голос
/ 11 апреля 2010

Родительский процесс использует поток ошибок и выходных данных дочернего процесса? Если в некоторых ОС дочерний процесс выводит некоторые ошибки / предупреждения на stdout / stderr и родительский процесс не использует потоки, дочерний процесс блокируется и не достигает System.exit ();

1 голос
/ 11 апреля 2010

Может быть плохо написанный финализатор? Моя первая мысль была отключена, когда я прочитал тему. Предположение: может ли поток, который перехватывает InterruptedException и продолжает работать, задерживать процесс выхода?

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

Вы уверены, что ребенок все еще действительно бежит и что это не просто нереализованный процесс зомби?

0 голосов
/ 28 января 2013

Еще один случай, не упомянутый здесь, - это если зависания отключаются на чем-то, поэтому будьте осторожны при написании кода прерывания отключения (и если сторонняя библиотека зарегистрировала также отключающий обработчик отключения).

Dean

0 голосов
/ 11 апреля 2010

Я думаю, что все очевидные причины были предварительно покрыты; например Финализаторы, перехватчики, неправильное сливание стандартного вывода / стандартной ошибки в родительском процессе. Теперь вам нужно больше доказательств, чтобы понять, что происходит.

Предложения:

  1. Настройте компьютер с Windows XP или Vista (или виртуальный), установите соответствующую JRE и ваше приложение и попытайтесь воспроизвести проблему. Как только вы сможете воспроизвести проблему, либо подключите отладчик, либо отправьте соответствующий сигнал, чтобы получить дамп потока для стандартной ошибки.

  2. Если вы не можете воспроизвести проблему, как описано выше, попросите одного из ваших пользователей получить дамп потока и переслать вам файл журнала.

...