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

88

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

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

Что насчет финализаторов, они тут помогают?

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

Ищу желательно портативные решения.

Ma99uS
источник
вы действительно должны четко определить, что вы имеете в виду. (пожалуйста)
Генри Би
7
Я предполагаю, что они имеют в виду, что они хотят иметь возможность очистить ресурсы, освободить, заблокировать и сбросить любые постоянные данные на диск, прежде чем программа будет уничтожена.
Стив g

Ответы:

82

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

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

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

jsight
источник
1
К сожалению, это не работает в Windows 7 (64-разрядная версия). Я пробовал использовать taskill без флага force и обнаружил следующую ошибку: «ОШИБКА: процесс с PID 14324 не может быть завершен. Причина: этот процесс можно завершить только принудительно (с параметром / F)». Если указать форсировку "/ f", процесс будет немедленно закрыт.
Джейсон Хантли,
Вы не можете полагаться на запущенные финализаторы.
Thorbjørn Ravn Andersen
47

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

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

Управляемое приложение:
запустите его со следующими параметрами
виртуальной
машины : -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port = 9999
-Dcom.sun.management.jmxremote.authenticate = false
-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");
    }
}
}

Управляющее приложение:
запустите его с остановкой или запуском в качестве аргумента командной строки

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();
    }
}
}


Вот и все. :-)

Ma99uS
источник
7

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


источник
3

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

  • В вашем приложении реализуйте ловушку отключения
  • Если вы хотите корректно завершить работу JVM, установите Java-агент, который вызывает 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

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

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

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

Стив г
источник
1
Итак, shutdownHook вызывается во ВСЕХ случаях? Что если "убить" процесс из оболочки?
Ma99uS
Ну, нет, если вы убиваете его -9 :)
Steve g
0

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

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

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

посторонний
источник
0

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

Ma99uS
источник