Что может заставить Java продолжать работать после системы.exit()?

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

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

Edit 1: Я заставил пользователя установить JDK, чтобы мы могли получить дамп потока от оскорбительной виртуальной машины. Пользователь сказал мне, что процесс VM исчезает из VisualVM, как только он нажимает на элемент " Quit в моем меню---но, согласно диспетчеру задач Windows, процесс не завершен, и независимо от того, как долго пользователь ждет (минуты, часы), он никогда не завершается.

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

8 ответов


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

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

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


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

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

...

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

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

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


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

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

Runtime.getRuntime().halt(0);

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

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

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


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


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


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

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

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

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


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

Дин