Как заставить отключение завершить выполнение процесса, запущенного из Eclipse
У меня есть крюк завершения работы в моем приложении (создан с использованием Runtime.getRuntime().addShutdownHook
). Однако, если я запускаю приложение из Eclipse, когда оно закрывается, ловушка выключения не выполняется.
Я думаю, это потому, что Eclipse отправляет в процесс эквивалент сигнала принудительного уничтожения, что не приводит к выполнению обработчика отключения (эквивалентного taskkill /F в Windows или kill -p в Linux), хотя я ' Я не совсем уверен.
Кто-нибудь знает, как это обойти? Я использую Windows (Vista), и я чувствую, что это может быть проблема, специфичная для Windows, но я не уверен.
7 ответов
Я использовал следующий хак в конце основного метода, чтобы обойти проблему:
if (Boolean.parseBoolean(System.getenv("RUNNING_IN_ECLIPSE"))) {
System.out.println("You're using Eclipse; click in this console and " +
"press ENTER to call System.exit() and run the shutdown routine.");
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
System.exit(0);
}
Прежде всего, ваше приложение заканчивается или вы принудительно заканчиваете его? Если вы принудительно завершаете его (с помощью кнопки "Стоп"), этот отчет об ошибке Eclipse содержит подробную информацию о том, почему это может не работать.
В противном случае вы можете быть правы в том, что это поведение, характерное для Windows. Я на Mac, поэтому не могу подтвердить, извините. Тем не менее, я могу сказать вам, что следующая тестовая программа выполняет ловушки завершения работы, как и ожидалось.
public class MyShutdownHook
{
public static void main( String[] args )
{
System.out.println( "Entering main." );
Runtime.getRuntime().addShutdownHook(
new Thread(
new Runnable() {
public void run() {
System.out.println( "Shutdown hook ran." );
}
}
)
);
System.out.println( "Exiting main." );
}
}
В Javadocs для Runtime#addShutdownHook упоминается, что перехватчики завершения работы не запускаются, когда JVM прерывается, обычно не завершается, поэтому, опять же, вы, вероятно, правы в своих предположениях. Это, как говорится, вот несколько вещей, чтобы попробовать. Опять же, извините, я не могу подтвердить это заранее - здесь нет Windows. (Благословенно!)
- Убедитесь, что JVM не включает опцию -Xrs. Это имеет побочный эффект на крюках отключения.
- Попробуйте запустить JVM, используя опцию -server или -client. По моему опыту, поведение в крайнем случае может зависеть от выбора режима виртуальной машины. (Особенно в отношении потоков - по крайней мере, на Mac.)
- Вы запускаете свое приложение под профилировщиком или тому подобное? Загрузка каких-либо нативных библиотек?
- Вы получаете какую-то фатальную ошибку и упускаете ее? Например, OutOfMemoryError или подобное?
- Попробуйте установить / снять флажок " Запуск в фоновом режиме" в диалоговом окне " Выполнить настройку" вашего приложения в Eclipse.
- И последнее, но не менее важное: если у вас есть возможность, вызовите System.exit() вручную.:)
Вот скрипт, который вы можете запустить за пределами Eclipse, чтобы вывести список доступных процессов, работающих в Eclipse, которые вы можете убить.
#!/bin/bash
set -o nounset # Treat unset variables as an error
PROCESSES=$(ps axo pid,ppid,command)
# Find eclipse launcher PID
LAUNCHER_PID=$(echo "$PROCESSES" | grep "/usr/lib/eclipse/eclipse" |grep -v "launcher"|awk '{print $1}')
echo "Launcher PID $LAUNCHER_PID"
# Find eclipse PID
ECLIPSE_PID=$(echo "$PROCESSES" | egrep "[[:digit:]]* $LAUNCHER_PID " | awk '{print $1}')
echo "Eclipse PID $ECLIPSE_PID"
# Find running eclipse sub-process PIDs
SUB_PROCESS=$(echo "$PROCESSES" | egrep "[[:digit:]]* $ECLIPSE_PID " | awk '{print $1}')
# List processes
echo
for PROCESS in $SUB_PROCESS; do
DRIVER=$(ps --no-headers o pid,ppid,command $PROCESS | awk '{print $NF}')
echo "$PROCESS $DRIVER"
done
echo "Kill a process using: 'kill -SIGTERM \$PID'"
В Windows, чтобы корректно остановить стандартное Java-приложение, вам нужно отправить Ctrl + C. Это работает только с консольными приложениями, но Eclipse использует javaw.exe
вместо java.exe
, Чтобы решить эту проблему, откройте конфигурацию запуска, вкладку JRE и выберите "Альтернативный JRE:". Появляется групповое окно "Исполняемый файл Java", позволяющее ввести альтернативный исполняемый файл "Java".
Теперь нам нужна внешняя программа для отправки Ctrl-C процессу со скрытой консолью. Я нашел подсказки здесь и здесь. Наша программа подключает к консоли нужный процесс и отправляет событие консоли.
#include <stdio.h>
#include <windows.h>
int main(int argc, char* argv[])
{
if (argc == 2) {
unsigned pid = 0;
if (sscanf_s(argv[1], "%u", &pid) == 1) {
FreeConsole(); // AttachConsole will fail if we don't detach from current console
if (AttachConsole(pid)) {
//Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(NULL, TRUE);
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
return 0;
}
}
}
return 1;
}
Тестовая Java-программа:
public class Shuthook {
public static void main(final String[] args) throws Exception {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
System.out.println("Shutting down...");
}
});
String sPid = ManagementFactory.getRuntimeMXBean().getName();
sPid = sPid.substring(0, sPid.indexOf('@'));
System.out.println("pid: " + sPid);
System.out.println("Sleeping...");
Thread.sleep(1000000);
}
}
Прекращение этого:
C:\>killsoft.exe 10520
Вывод тестовой программы в Eclipse:
pid: 10520
Sleeping...
Shutting down...
Я не уверен, как это исправить, но IntelliJ добавил отдельную кнопку в их диалоговое окно "Выполнить", которое отключит виртуальную машину таким образом, что вызывает "Завершение работы". Их отладчик не имеет этой функции.
Я застрял в сети в данный момент, и не вижу, что я ищу. Но я помню, что у Eclipse была опция конфигурации запуска, связанная с запуском приложения на той же виртуальной машине. Возможно ли, что вы запускаете Java-приложение на той же виртуальной машине, что и виртуальная машина eclipse?
Но вариант ускользает от меня.
Сайрам прямо здесь, вызывая System.exit(0), мы можем завершить JVM затмения и увидеть результаты перехвата завершения работы.