Как остановить процесс Java изящно? - PullRequest
85 голосов
/ 10 октября 2008

Как изящно остановить процесс Java в Linux и Windows?

Когда вызывается Runtime.getRuntime().addShutdownHook, а когда нет?

А как насчет финализаторов, они здесь помогают?

Могу ли я отправить какой-то сигнал процессу Java из оболочки?

Я ищу предпочтительно портативные решения.

Ответы [ 7 ]

78 голосов
/ 10 октября 2008

Завершающие перехватчики выполняются во всех случаях, когда виртуальная машина не была принудительно уничтожена. Таким образом, если вы выполните «стандартное» уничтожение (SIGTERM из команды kill), они будут выполнены. Аналогичным образом они будут выполняться после вызова System.exit(int).

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

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

47 голосов
/ 16 октября 2008

Хорошо, после всех возможностей, которые я выбрал для работы с «Java Monitoring and Management»
Обзор здесь
Это позволяет вам относительно легко управлять одним приложением из другого. Вы можете вызвать управляющее приложение из скрипта, чтобы изящно остановить контролируемое приложение перед его уничтожением.

Вот упрощенный код:

Контролируемое приложение:
запустите его с параметрами VM:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port = 9999
-Dcom.sun.management.jmxremote.authenticate = ложь
-Dcom.sun.management.jmxremote.ssl = ложь

//ThreadMonitorMBean.java
public interface ThreadMonitorMBean
{
String getName();
void start();
void stop();
boolean isRunning();
}

// ThreadMonitor.java
public class ThreadMonitor implements ThreadMonitorMBean
{
private Thread m_thrd = null;

public ThreadMonitor(Thread thrd)
{
    m_thrd = thrd;
}

@Override
public String getName()
{
    return "JMX Controlled App";
}

@Override
public void start()
{
    // TODO: start application here
    System.out.println("remote start called");
}

@Override
public void stop()
{
    // TODO: stop application here
    System.out.println("remote stop called");

    m_thrd.interrupt();
}

public boolean isRunning()
{
    return Thread.currentThread().isAlive();
}

public static void main(String[] args)
{
    try
    {
        System.out.println("JMX started");

        ThreadMonitorMBean monitor = new ThreadMonitor(Thread.currentThread());

        MBeanServer server = ManagementFactory.getPlatformMBeanServer();

        ObjectName name = new ObjectName("com.example:type=ThreadMonitor");

        server.registerMBean(monitor, name);

        while(!Thread.interrupted())
        {
            // loop until interrupted
            System.out.println(".");
            try 
            {
                Thread.sleep(1000);
            } 
            catch(InterruptedException ex) 
            {
                Thread.currentThread().interrupt();
            }
        }
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }
    finally
    {
        // TODO: some final clean up could be here also
        System.out.println("JMX stopped");
    }
}
}

Приложение управления:
запустите его с stop или start в качестве аргумента командной строки

public class ThreadMonitorConsole
{

public static void main(String[] args)
{
    try
    {   
        // connecting to JMX
        System.out.println("Connect to JMX service.");
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:9999/jmxrmi");
        JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();

        // Construct proxy for the the MBean object
        ObjectName mbeanName = new ObjectName("com.example:type=ThreadMonitor");
        ThreadMonitorMBean mbeanProxy = JMX.newMBeanProxy(mbsc, mbeanName, ThreadMonitorMBean.class, true);

        System.out.println("Connected to: "+mbeanProxy.getName()+", the app is "+(mbeanProxy.isRunning() ? "" : "not ")+"running");

        // parse command line arguments
        if(args[0].equalsIgnoreCase("start"))
        {
            System.out.println("Invoke \"start\" method");
            mbeanProxy.start();
        }
        else if(args[0].equalsIgnoreCase("stop"))
        {
            System.out.println("Invoke \"stop\" method");
            mbeanProxy.stop();
        }

        // clean up and exit
        jmxc.close();
        System.out.println("Done.");    
    }
    catch(Exception e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
}


Вот и все. : -)

7 голосов
/ 17 июня 2009

Другой способ: ваше приложение может открыть серверный socet и дождаться поступления информации на него. Например, строка с «волшебным» словом :), а затем реагирует на завершение работы: System.exit (). Вы можете отправить такую ​​информацию в гнездо с помощью внешнего приложения, такого как telnet.

2 голосов
/ 16 июня 2016

Вот немного хитрое, но портативное решение:

  • В вашем приложении реализовать отключающий хук
  • Если вы хотите корректно завершить работу JVM, установите Java Agent , который вызывает System.exit () с использованием Attach API .

Я реализовал агент Java. Доступно на Github: https://github.com/everit-org/javaagent-shutdown

Подробное описание решения доступно здесь: https://everitorg.wordpress.com/2016/06/15/shutting-down-a-jvm-process/

2 голосов
/ 10 октября 2008

Аналогичный вопрос Здесь

Финализаторы на Java плохие. Они добавляют много накладных расходов на сборку мусора. Избегайте их, когда это возможно.

shutdownHook вызывается только при выключении виртуальной машины. Я думаю, что это очень хорошо, может делать то, что вы хотите.

0 голосов
/ 10 октября 2008

Спасибо за ваши ответы. Отключение крючков швов, как то, что будет работать в моем случае. Но я также столкнулся с вещью, называемой компонентами Мониторинг и Управление:
http://java.sun.com/j2se/1.5.0/docs/guide/management/overview.html
Это дает хорошие возможности для удаленного мониторинга и манипулирования процессом Java. (Был введен в Java 5)

0 голосов
/ 10 октября 2008

Сигнализация в Linux может быть выполнена с помощью команды kill (man kill для доступных сигналов), для этого вам потребуется идентификатор процесса. (ps ax | grep java) или что-то в этом роде, или сохраняйте идентификатор процесса при создании процесса (это используется в большинстве файлов запуска linux, см. /etc/init.d)

Переносимая сигнализация может быть сделана путем интеграции SocketServer в ваше Java-приложение. Это не так сложно и дает вам свободу отправлять любые команды, которые вы хотите.

Если вы имели в виду предложения finally вместо финализаторов; они не получают расширения при вызове System.exit (). Финализаторы должны работать, но на самом деле не должны делать ничего более значительного, кроме как печатать оператор отладки. Они опасны.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...