Jak z wdziękiem zatrzymać proces java?

88

Jak bezpiecznie zatrzymać proces Java w systemie Linux i Windows?

Kiedy dzwonią Runtime.getRuntime().addShutdownHook, a kiedy nie?

A co z finalizatorami, czy oni tu pomagają?

Czy mogę wysłać jakiś sygnał do procesu Java z powłoki?

Szukam najlepiej przenośnych rozwiązań.

Ma99uS
źródło
powinieneś naprawdę zdefiniować, co masz na myśli, mówiąc wdzięcznie. (proszę)
Henry B
7
Zakładam, że mają na myśli, że chcą mieć możliwość wyczyszczenia zasobów, zwolnienia, zablokowania i opróżnienia wszelkich trwałych danych na dysk, zanim program zostanie zabity.
Steve g

Odpowiedzi:

82

Haki zamykania są wykonywane we wszystkich przypadkach, gdy maszyna wirtualna nie jest zabijana na siłę. Tak więc, jeśli miałbyś wydać „standardowe” zabicie ( SIGTERMz polecenia zabicia), to wykonają. Podobnie wykonają się po wywołaniu System.exit(int).

Jednak twarde zabicie ( kill -9lub kill -SIGKILL) nie zostanie wykonane. Podobnie (i oczywiście) nie wykonają się, jeśli wyciągniesz zasilanie z komputera, wrzucisz go do kadzi z wrzącą lawą lub rozbijesz procesor na kawałki młotem. Prawdopodobnie już to wiedziałeś.

Finalizatory naprawdę również powinny działać, ale najlepiej nie polegać na tym podczas czyszczenia po zamknięciu, ale raczej polegać na hakach zamykania, aby zatrzymać wszystko w czysty sposób. I, jak zawsze, uważaj na zakleszczenia (widziałem zbyt wiele haków zamykających zawieszających cały proces)!

jsight
źródło
1
Niestety, to nie działa w systemie Windows 7 (64-bitowym). Próbowałem użyć funkcji taskill bez flagi wymuszenia i napotkałem następujący błąd: „BŁĄD: Nie można zakończyć procesu z PID 14324. Przyczyna: ten proces można zakończyć tylko na siłę (z opcją / F)”. Podanie opcji siły, "/ f", oczywiście natychmiast zamknie proces.
Jason Huntley
Nie można polegać na uruchamianiu finalizatorów.
Thorbjørn Ravn Andersen
47

Ok, po tych wszystkich możliwościach, które wybrałem do pracy z "Java Monitoring and Management"
Przegląd jest tutaj,
który pozwala w stosunkowo łatwy sposób kontrolować jedną aplikację od drugiej. Możesz wywołać aplikację sterującą ze skryptu, aby bezpiecznie zatrzymać kontrolowaną aplikację przed jej zabiciem.

Oto uproszczony kod:

Aplikacja kontrolowana:
uruchom ją z następującymi parametrami maszyny wirtualnej:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port = 9999
-Dcom.sun.management.jmxremote.authenticate = false
-Dcom.sun.management. jmxremote.ssl = false

//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");
    }
}
}

Kontrolowanie aplikacji:
uruchom ją ze stop lub start jako argumentem wiersza poleceń

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


Otóż ​​to. :-)

Ma99uS
źródło
7

Inny sposób: Twoja aplikacja może otworzyć społeczność serwerów i czekać na nadejście informacji. Na przykład łańcuch z "magicznym" słowem :), a następnie zareaguj, aby zamknąć: System.exit (). Możesz wysłać takie informacje do gniazda za pomocą zewnętrznej aplikacji, takiej jak telnet.


źródło
3

Oto trochę trudne, ale przenośne rozwiązanie:

  • W swojej aplikacji zaimplementuj hak zamykający
  • Jeśli chcesz bezpiecznie zamknąć maszynę JVM, zainstaluj agenta Java, który wywołuje System.exit () za pomocą interfejsu Attach API .

Zaimplementowałem agenta Java. Jest dostępny na Github: https://github.com/everit-org/javaagent-shutdown

Szczegółowy opis rozwiązania dostępny jest tutaj: https://everitorg.wordpress.com/2016/06/15/shutting-down-a-jvm-process/

Balazs Zsoldos
źródło
2

Podobne pytanie tutaj

Finalizatory w Javie są złe. Dodają dużo narzutów do zbierania śmieci. Unikaj ich, gdy tylko jest to możliwe.

ShutdownHook zostanie wywołany tylko podczas zamykania maszyny wirtualnej. Myślę, że bardzo dobrze może zrobić, co chcesz.

Steve g
źródło
1
Więc shutdownHook jest wywoływany we WSZYSTKICH przypadkach? Co jeśli „zabije” proces z powłoki?
Ma99uS
Cóż, nie, jeśli dajesz mu zabójstwo -9 :)
Steve g
0

Sygnalizacja w Linuksie może być wykonana za pomocą "kill" (man kill dla dostępnych sygnałów), do tego potrzebny byłby identyfikator procesu. (ps ax | grep java) lub coś w tym rodzaju, lub przechowuj identyfikator procesu, gdy proces zostanie utworzony (jest to używane w większości plików startowych Linuksa, patrz /etc/init.d)

Sygnalizacja przenośna może zostać zrealizowana poprzez integrację SocketServer z aplikacją Java. Nie jest to takie trudne i daje swobodę wysyłania dowolnych poleceń.

Jeśli miałeś na myśli klauzule ostateczne zamiast finalizatorów; nie są uruchamiane, gdy wywoływana jest funkcja System.exit (). Finalizatory powinny działać, ale tak naprawdę nie powinny robić nic bardziej znaczącego, ale drukować instrukcję debugowania. Są niebezpieczne.

extraneon
źródło
0

Dzięki za odpowiedzi. Haczyki zamykające szwy jak coś, co by działało w moim przypadku. Ale natknąłem się również na coś, co nazywa się fasolami monitorowania i zarządzania:
http://java.sun.com/j2se/1.5.0/docs/guide/management/overview.html
Daje to kilka fajnych możliwości zdalnego monitorowania i manipulacji procesu java. (Został wprowadzony w Javie 5)

Ma99uS
źródło