Як витончено зупинити процес Java?


88

Як витончено зупинити процес Java у Linux та Windows?

Коли Runtime.getRuntime().addShutdownHookтелефонують, а коли ні?

А як щодо фіналізаторів, вони тут допомагають?

Чи можу я надіслати якийсь сигнал до процесу Java із оболонки?

Я шукаю переважно портативні рішення.


ви повинні дійсно визначити, що ви маєте на увазі під витончено. (будь ласка)
Генрі Б

7
Я припускаю, що вони мають на увазі, що хочуть отримати можливість очистити ресурси, випустити, заблокувати та очистити постійні дані на диск до того, як програму буде вбито.
Steve g

Відповіді:


82

Гаки відключення виконуються у всіх випадках, коли віртуальна машина не примусово вбита. Отже, якщо ви хочете випустити "стандартне" вбивство ( SIGTERMз команди kill), тоді вони виконають. Подібним чином вони будуть виконуватися після виклику System.exit(int).

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

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


1
На жаль, це не працює в Windows 7 (64-бітна версія). Я спробував використати taskill, без прапорця сили, і зіткнувся з такою помилкою: "ПОМИЛКА: Процес з PID 14324 не може бути завершений. Причина: Цей процес може бути припинено лише примусово (з опцією / F)." Подання опції сили "/ f", очевидно, негайно закриє процес.
Джейсон Хантлі,

Ви не можете покладатися на запущені фіналізатори.
Thorbjørn Ravn Andersen

47

Гаразд, після всіх можливостей, які я вибрав для роботи з «Моніторингом та управлінням Java»
, тут
з’явився Огляд, що дозволяє управляти одним додатком з іншого порівняно простим способом. Ви можете зателефонувати керуючій програмі зі сценарію, щоб витончено зупинити керовану програму перед її вбивством.

Ось спрощений код:

Керований додаток:
запустіть його з наступними параметрами ВМ:
-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();
    }
}
}


Це воно. :-)


7

Інший спосіб: ваша програма може відкрити серверний Socet і чекати отримання інформації. Наприклад рядок з "чарівним" словом :), а потім відреагуйте, щоб зробити вимкнення: System.exit (). Ви можете надіслати таку інформацію в розетку, використовуючи зовнішню програму, наприклад 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 :)
Стів г

0

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

Портативна сигналізація може бути здійснена шляхом інтеграції SocketServer у вашу програму Java. Це не так складно і дає вам свободу надіслати будь-яку команду, яку ви хочете.

Якщо ви мали на увазі остаточні пропозиції замість фіналізаторів; вони не отримують розширення, коли викликається System.exit (). Фіналізатори повинні працювати, але насправді не повинні робити нічого більш значущого, крім друку оператора налагодження. Вони небезпечні.


0

Дякую за відповіді. Відключення гачків, як щось, що могло б спрацювати в моєму випадку. Але я також наткнувся на те, що називається компонентами моніторингу та управління:
http://java.sun.com/j2se/1.5.0/docs/guide/management/overview.html
Це дає гарні можливості для віддаленого моніторингу та маніпуляцій Java-процесу. (Було представлено в Java 5)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.