90

如何在 Linux 和 Windows 中优雅地停止 Java 进程?

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

终结者呢,他们在这里有帮助吗?

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

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

4

7 回答 7

84

关闭挂钩在 VM 未被强制终止的所有情况下执行。因此,如果您要发出“标准”kill(SIGTERM来自 kill 命令),那么它们将执行。同样,它们将在调用后执行System.exit(int)

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

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

于 2008-10-10T15:47:22.923 回答
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();
    }
}
}


就是这样。:-)

于 2008-10-16T14:36:01.690 回答
7

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

于 2009-06-17T15:19:03.667 回答
3

这是一个有点棘手但可移植的解决方案:

  • 在您的应用程序中实现关闭挂钩
  • 当您想要优雅地关闭 JVM 时,请安装使用Attach API调用System.exit()的Java 代理

我实现了 Java 代理。它在 Github 上可用:https ://github.com/everit-org/javaagent-shutdown

有关该解决方案的详细说明,请参见此处:https ://everitorg.wordpress.com/2016/06/15/shutting-down-a-jvm-process/

于 2016-06-16T08:33:10.707 回答
2

类似的问题在这里

Java 中的终结器很糟糕。它们为垃圾收集增加了很多开销。尽可能避免使用它们。

shutdownHook 只会在 VM 关闭时被调用。我认为它可以做你想要的。

于 2008-10-10T13:23:57.393 回答
0

Linux 中的信号可以通过“kill”(可用信号的 man kill)来完成,您需要进程 ID 来执行此操作。(ps ax | grep java) 或类似的东西,或者在创建进程时存储进程 ID(这在大多数 linux 启动文件中使用,请参阅 /etc/init.d)

可移植信号可以通过在您的 java 应用程序中集成一个 SocketServer 来完成。这并不难,让您可以自由发送您想要的任何命令。

如果您的意思是 finally 子句而不是终结器;当 System.exit() 被调用时,它们不会被执行。终结器应该可以工作,但实际上不应该做任何更重要的事情,而是打印一个调试语句。他们很危险。

于 2008-10-10T14:37:42.707 回答
0

谢谢你的回答。关机钩接缝就像我的情况一样。但我也碰到了一个叫做监控和管理 bean 的东西:http:
//java.sun.com/j2se/1.5.0/docs/guide/management/overview.html
这为远程监控和操作提供了一些很好的可能性的java进程。(在 Java 5 中引入)

于 2008-10-10T17:11:23.910 回答