Как заставить отключение завершить выполнение процесса, запущенного из 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 затмения и увидеть результаты перехвата завершения работы.

Другие вопросы по тегам