How to stop java process gracefully?

195

Solution 1

Shutdown hooks execute in all cases where the VM is not forcibly killed. So, if you were to issue a "standard" kill (SIGTERM from a kill command) then they will execute. Similarly, they will execute after calling System.exit(int).

However a hard kill (kill -9 or kill -SIGKILL) then they won't execute. Similarly (and obviously) they won't execute if you pull the power from the computer, drop it into a vat of boiling lava, or beat the CPU into pieces with a sledgehammer. You probably already knew that, though.

Finalizers really should run as well, but it's best not to rely on that for shutdown cleanup, but rather rely on your shutdown hooks to stop things cleanly. And, as always, be careful with deadlocks (I've seen far too many shutdown hooks hang the entire process)!

Solution 2

Ok, after all the possibilities I have chosen to work with "Java Monitoring and Management"
Overview is here
That allows you to control one application from another one in relatively easy way. You can call the controlling application from a script to stop controlled application gracefully before killing it.

Here is the simplified code:

Controlled application:
run it with the folowing VM parameters:
-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");
    }
}
}

Controlling application:
run it with the stop or start as the command line argument

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


That's it. :-)

Solution 3

Here is a bit tricky, but portable solution:

  • In your application implement a shutdown hook
  • When you want to shut down your JVM gracefully, install a Java Agent that calls System.exit() using the Attach API.

I implemented the Java Agent. It is available on Github: https://github.com/everit-org/javaagent-shutdown

Detailed description about the solution is available here: https://everitorg.wordpress.com/2016/06/15/shutting-down-a-jvm-process/

Solution 4

Similar Question Here

Finalizers in Java are bad. They add a lot of overhead to garbage collection. Avoid them whenever possible.

The shutdownHook will only get called when the VM is shutting down. I think it very well may do what you want.

Share:
195
AdsWhite
Author by

AdsWhite

Updated on November 04, 2020

Comments

  • AdsWhite
    AdsWhite over 3 years

    Consider a scenario where users can have multiple roles with different organisations.

    The tables are:

    USER - has an Id column
    ORGANISATION - has an Id column
    ROLE - has an Id column
    USERROLE - has a User_Id column, an Organisation_Id column and a Role_Id column

    (So for example, User Bob can have roles 1, 2 and 3 with ABC Inc and role 1 with XYZ Plc)

    How do you only return users who have roles 1, 2 and 3? I don't mind if they have other roles as well or multiples of 1, 2 or 3 but they must at least have 1 of each role 1, 2 and 3.

  • Ma99uS
    Ma99uS over 15 years
    So shutdownHook gets called in ALL cases? What if 'kill' the process from shell?
  • Steve g
    Steve g over 15 years
    Well, not if you're giving it a kill -9 :)
  • AdsWhite
    AdsWhite over 10 years
    Bingo! Exactly what I was after.
  • Jason Huntley
    Jason Huntley about 9 years
    Unfortunately, this doesn't seem to work on Windows 7 (64bit). I've tried using taskill, without the force flag, and encounter the following error: "ERROR: The process with PID 14324 could not be terminated. Reason: This process can only be terminated forcefully (with /F option)." Supplying the force option, "/f", obviously will close the process instantly.
  • matbrgz
    matbrgz over 7 years
    You cannot rely on finalizers being run.