web-dev-qa-db-ja.com

Javaプロセスを正常に停止するには?

LinuxおよびWindowsでJavaプロセスを正常に停止するにはどうすればよいですか?

Runtime.getRuntime().addShutdownHookはいつ呼び出され、いつ呼び出されませんか?

ファイナライザーはどうですか、ここで役立ちますか?

シェルからJavaプロセスに何らかのシグナルを送信できますか?

できればポータブルなソリューションを探しています。

85
Ma99uS

シャットダウンフックは、VMが強制的に強制終了されないすべての場合に実行されます。したがって、「標準」強制終了(killコマンドからのSIGTERM)を発行すると、実行されます。同様に、 System.exit(int)を呼び出した後に実行されます。

ただし、ハードキル(kill -9 または kill -SIGKILL)その後、実行されません。同様に(そして明らかに)、コンピューターから電源を引いたり、沸騰した溶岩の大intoに落としたり、CPUをハンマーで叩いたりすると、実行されません。おそらく既に知っているでしょう。

ファイナライザも実際に実行する必要がありますが、シャットダウンのクリーンアップにはこれに依存せず、シャットダウンフックに依存して物事をきれいに停止するのが最善です。そして、いつものように、デッドロックには注意してください(プロセス全体を停止するシャットダウンフックが多すぎるのを見てきました)!

79
jsight

さて、「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");
    }
}
}

アプリケーションの制御:
コマンドライン引数としてstopまたはstartを指定して実行します

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


それでおしまい。 :-)

47
Ma99uS

別の方法:アプリケーションはサーバーsocetを開き、情報が到着するのを待つことができます。たとえば、「魔法の」Wordを含む文字列:)に反応して、シャットダウンを行います:System.exit()。 telnetなどの外部アプリケーションを使用して、このような情報を靴下に送信できます。

7
Ermak

同様の質問 ここ

Javaのファイナライザは不良です。ガベージコレクションに多くのオーバーヘッドが追加されます。可能な限り避けてください。

ShutdownHookは、VMがシャットダウンしている場合にのみ呼び出されます。あなたが望むことを非常にうまく行えると思います。

2
Steve g

これは少しトリッキーですが、ポータブルなソリューションです。

  • アプリケーションにシャットダウンフックを実装します
  • JVMを正常にシャットダウンする場合は、 Java Agent をインストールして、System.exit()を呼び出します- Attach API

JavaAgent。Githubで利用可能です: https://github.com/everit-org/javaagent-shutdown

ソリューションの詳細な説明はこちらから入手できます。 https://everitorg.wordpress.com/2016/06/15/shutting-down-a-jvm-process/

2
Balazs Zsoldos

回答ありがとうございます。シャットダウンは、私の場合に機能するもののように縫い目をフックします。しかし、私はMonitoring and Management Beanと呼ばれるものにもぶつかりました。
http://Java.Sun.com/j2se/1.5.0/docs/guide/management/overview.html
これは、リモート監視、およびJavaプロセスの操作のために、いくつかの素晴らしい可能性を与えます。(Java 5で導入されました)

0
Ma99uS

Linuxでのシグナル送信は「kill」(使用可能なシグナルの場合はman kill)で実行できます。そのためにはプロセスIDが必要です。 (ps ax | grep Java)またはそのようなもの、またはプロセスの作成時にプロセスIDを保存します(これはほとんどのLinuxスタートアップファイルで使用されます。/etc/init.dを参照)

JavaアプリケーションにSocketServerを統合することにより、ポータブルシグナリングを実行できます。それほど難しくはなく、必要なコマンドを自由に送信できます。

ファイナライザーの代わりに最終節を意味する場合。 System.exit()が呼び出されても、それらは実行されません。ファイナライザは動作するはずですが、実際にはこれ以上重要なことはしないで、デバッグステートメントを出力する必要があります。彼らは危険です。

0
extraneon