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


88

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

いつRuntime.getRuntime().addShutdownHook呼び出され、いつ呼び出されないのですか?

ファイナライザについてはどうですか、それらはここで役立ちますか?

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

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


あなたは本当にあなたが意味することを優雅に定義すべきです。(お願いします)
ヘンリーB

7
プログラムが終了する前に、リソースをクリーンアップし、解放し、ロックし、永続データをディスクにフラッシュする機会が与えられることを望んでいると思います。
スティーブg

回答:


82

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

ただし、強制終了(kill -9またはkill -SIGKILL)すると、実行されません。同様に(そして明らかに)、コンピューターから電源を引いたり、沸騰した溶岩の槽に落としたり、CPUを大ハンマーで叩いたりしても、実行されません。あなたはおそらくすでにそれを知っていました。

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


1
残念ながら、これはWindows 7(64ビット)では機能しないようです。強制フラグなしでtaskillを使用しようとしたところ、次のエラーが発生しました。強制オプション "/ f"を指定すると、プロセスが即座に終了します。
Jason Huntley 2015年

ファイナライザーの実行に依存することはできません。
–ThorbjørnRavn Andersen 2017

47

わかりました。結局、「Javaの監視と管理」で作業することを選択したすべての可能性の
概要がここにあります。
これにより、あるアプリケーションを別のアプリケーションから比較的簡単な方法で制御できます。スクリプトから制御アプリケーションを呼び出して、制御アプリケーションを強制終了する前に正常に停止できます。

簡略化したコードは次のとおりです。

制御アプリケーション:
:folowing 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();
    }
}
}


それでおしまい。:-)


7

別の方法:アプリケーションはサーバーソケットを開き、情報が届くのを待つことができます。たとえば、「魔法の」単語を含む文字列:)を使用してシャットダウンすると、System.exit()になります。Telnetなどの外部アプリケーションを使用して、そのような情報をsockeに送信できます。


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/


2

ここで同様の質問

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

shutdownHookは、VMがシャットダウンしているときにのみ呼び出されます。私はそれがあなたが望むことをうまくやるかもしれないと思います。


1
それで、shutdownHookはすべての場合に呼び出されますか?シェルからプロセスを「強制終了」するとどうなりますか?
Ma99uS 2008年

まあ、あなたがそれを-9で殺しているのではないなら:)
Steve g

0

Linuxでのシグナリングは、「kill」(使用可能なシグナルのman kill)で実行できます。これを行うには、プロセスIDが必要です。(ps ax | grep java)またはそのようなもの、またはプロセスが作成されたときにプロセスIDを保存します(これはほとんどのLinux起動ファイルで使用されます。/etc/init.dを参照してください)

ポータブルシグナリングは、SocketServerをJavaアプリケーションに統合することで実行できます。それはそれほど難しくなく、あなたが望むどんなコマンドでも送る自由を与えます。

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


0

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.