如何优雅地停止Java进程?


88

如何在Linux和Windows中正常停止Java进程?

什么时候Runtime.getRuntime().addShutdownHook被调用,什么时候不被调用?

终结器又如何呢?

我可以从外壳向Java进程发送某种信号吗?

我正在寻找最好的便携式解决方案。


您应该真正优雅地定义您的意思。(请)
Henry B

7
我以为他们的意思是说他们希望有机会在程序被杀死之前清理资源,释放,锁定并将所有持久性数据刷新到磁盘。
史蒂夫·克

Answers:


82

在所有未强制终止VM的情况下,都会执行关机挂钩。因此,如果要发出“标准” kill(SIGTERM通过kill命令),则它们将执行。同样,它们将在调用后执行System.exit(int)

但是强行杀死(kill -9kill -SIGKILL),然后它们将不会执行。同样(很明显),如果您从计算机上取下电源,将其放入沸腾的熔岩桶中或用大锤将CPU粉碎成碎片,它们将不会执行。您可能已经知道了。

终结器确实也应该运行,但是最好不要依赖于终结器来进行关闭清理,而要依靠您的关闭钩子来干净地停止运行。而且,一如既往,要小心死锁(我已经看到太多的关闭挂钩会挂起整个过程)!


1
不幸的是,这似乎不适用于Windows 7(64位)。我尝试使用没有强制标志的Taskill并遇到以下错误:“错误:无法终止PID为14324的进程。原因:只能强制终止此进程(使用/ F选项)。” 提供强制选项“ / f”显然会立即关闭该过程。
杰森·亨特利

您不能依靠正在运行的终结器。
托尔比约恩Ravn的安德森

47

好的,在我选择了使用“ Java监视和管理”之后,所有的可能性都
在这里概述
,使您可以相对容易地从一个应用程序控制另一个应用程序。您可以从脚本中调用控制应用程序,以在杀死应用程序之前正常停止它。

这是简化的代码:

受控的应用程序:
使用以下VM参数运行它:
-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");
    }
}
}

控制应用程序:
stopstart作为命令行参数运行它

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

另一种方式:您的应用程序可以打开服务器套接字并等待信息到达。例如,一个带有“ magic”字词的字符串:),然后作出反应以关闭:System.exit()。您可以使用诸如telnet之类的外部应用程序将此类信息发送到袜子。



2

类似问题 在这里

Java中的终结器是不好的。它们为垃圾回收增加了很多开销。尽可能避免使用它们。

仅当虚拟机关闭时,shutdownHook才会被调用。我认为这可能会做您想要的。


1
那么在所有情况下都将调用shutdownHook吗?如果从shell中“杀死”进程怎么办?
Ma99uS

好吧,如果您要杀死它的话不是-9 :)
史蒂夫·克

0

Linux中的信号发送可以用“ kill”(可用信号杀死),您需要进程ID来完成。(ps ax | grep java)之类的东西,或者在创建进程时存储进程ID(在大多数linux启动文件中使用,请参见/etc/init.d)

可移植的信号可以通过将SocketServer集成到您的Java应用程序中来完成。并不是那么困难,它使您可以自由发送所需的任何命令。

如果您要用finally子句代替finalizer;调用System.exit()时,它们不会被保护。终结器应该可以工作,但除了打印调试语句外,实际上不应该做其他更重要的事情。他们很危险。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.