Подтвердить что ты не робот

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

У меня есть 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, чтобы мы могли получить дамп потока от нарушительной виртуальной машины. Мне сказали, что процесс VM исчезает из VisualVM, как только он нажимает на элемент "Выход" в моем меню --- но, согласно диспетчеру задач Windows, процесс не прекращается и не зависит от того, как долго пользователь ждет (минуты, часы), он никогда не заканчивается.

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

Runtime.getRuntime().halt(0);

Ответ 3

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

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

...

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

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

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

Ответ 4

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

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

Вы уверены, что ребенок по-прежнему работает, и что это не просто процесс незавершенного зомби?

Ответ 5

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

Ответ 6

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

Ответ 7

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

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

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

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

Ответ 8

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

Дин

Ответ 9

Проверьте, нет ли тупика.

Например,

Runtime.getRuntime().addShutdownHook(new Thread(this::close));
System.exit(1);

и в close()

public void close() {
// some code
System.exit(1);
}

System.exit() фактически вызывает обработчик завершения работы и финализаторы. Поэтому, если ваш хук завершения работы снова вызывает внутреннюю функцию exit() по какой-либо причине (например, когда close() вызывает исключение, для которого вы хотите выйти из программы, вы также вызовете там exit()). Вот так..

public void close() {
  try {
  // some code that raises exception which requires to exit the program
  } catch(Exception exceptionWhichWhenOccurredShouldExitProgram) {
    log.error(exceptionWhichWhenOccurredShouldExitProgram);
    System.exit(1);
  }
}

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

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

В любом случае, если это так, проблему можно решить с помощью этого обходного пути:

private static AtomicBoolean exitCalled=new AtomicBoolean();

    private static void exit(int status) {
        if(!exitCalled.get()) {
            exitCalled.set(true);
            System.exit(status);
        }
    }

    Runtime.getRuntime().addShutdownHook(new Thread(MyClass::close));
      exit(1);
    }

    private static void close() {
        exit(1);
    }

П.С.: Я чувствую, что вышеупомянутая версия exit() должна быть написана только в методе System.exit() (может быть, это какой-то PR для JDK?), потому что, практически нет смысла (по крайней мере из того, что я вижу) в развлекать тупик в System.exit()