web-dev-qa-db-ja.com

System.exit()の後もJavaが実行され続ける原因は何ですか?

別のJavaプログラムからProcessBuilderを介して開始されているJavaプログラムがあります。System.exit(0)はから呼び出されます子プログラムですが、一部のユーザー(Windowsの場合)では、子に関連付けられた_Java.exe_プロセスが終了しません。子プログラムにはシャットダウンフックがなく、SecurityManagerもありません。 System.exit()によるVMの終了を停止します。LinuxまたはWindowsVistaで問題を自分で再現することはできません。これまでのところ、問題の報告は2つのWindows XP 2つの異なるJRE(1.6.0_15と1.6.0_18)を使用するユーザーと1人のVistaユーザーですが、毎回問題を再現することができます。

System.exit()の後で、一部のマシンでのみJVMが終了しない理由を誰かが提案できますか?

編集1:問題のあるVMからスレッドダンプを取得できるように、ユーザーにJDKをインストールしてもらいました。ユーザーが私に言ったことは、VMプロセスは、メニューの[終了]項目をクリックするとすぐにVisualVMから消えます---しかし、Windowsタスクマネージャーによると、プロセスは終了せず、ユーザーがどれだけ待機しても(分、時間)、終了することはありません。

編集2:親プログラムのProcess.waitFor()が、問題のあるユーザーの少なくとも1人に対して決して返されないことを確認しました。したがって、要約すると、子VMは死んでいるように見えます(VisualVMはそれを認識していません)が、親はプロセスをライブとして認識し、Windowsも同様です。

22
uckelman

親プロセスには、子のSTDOUTとSTDERR(その出力をログファイルに渡す)のそれぞれを消費するための専用のスレッドが1つあります。私が見る限り、ログに表示されると予想されるすべての出力が表示されているため、これらは正常に機能しています。

stdout/stderrを使用しているときに、プログラムがタスクマネージャーから消えないという同様の問題がありました。私の場合、system.exit()を呼び出す前にリッスンしていたストリームを閉じると、javaw.exeがハングしました。奇妙なことに、それはストリームに書き込んでいませんでした...

私の場合の解決策は、存在する前にストリームを閉じるのではなく、単にストリームをフラッシュすることでした。もちろん、終了する前に、いつでもフラッシュしてからstdoutとstderrにリダイレクトすることができます。

6
Andrew McVeigh

これは、コード(または使用するライブラリ)にシャットダウンフックまたはファイナライザーがあり、正常に終了しない場合に発生する可能性があります。

シャットダウンを強制するためのより強力な(したがって、極端な場合にのみ使用する必要があります!)方法は、次のコマンドを実行することです。

Runtime.getRuntime().halt(0);
12
Molten Ice

ここにいくつかのシナリオがあります...

http://Java.Sun.com/j2se/1.4.2/docs/api/Java/lang/Thread.html のスレッドの定義による

.。

Java仮想マシンが起動すると、通常、デーモン以外のスレッドが1つあります(通常、指定されたクラスのmainという名前のメソッドを呼び出します)。Java =仮想マシンは、次のいずれかが発生するまでスレッドを実行し続けます。

1)クラスRuntimeのexitメソッドが呼び出され、セキュリティマネージャがexit操作の実行を許可しました。 2)デーモンスレッドではないすべてのスレッドが、runメソッドの呼び出しから戻るか、runメソッドを超えて伝播する例外をスローすることによって停止しました。

もう1つの可能性は、メソッドrunFinalizersOnExitが呼び出された場合です。 http://Java.Sun.com/j2se/1.4.2/docs/api/Java/lang/System.html のドキュメントに従って非推奨。この方法は本質的に安全ではありません。ライブオブジェクトでファイナライザーが呼び出され、他のスレッドがそれらのオブジェクトを同時に操作していると、動作が不安定になったり、デッドロックが発生したりする可能性があります。終了時のファイナライズを有効または無効にします。そうすることで、ファイナライザーがまだ自動的に呼び出されていないすべてのオブジェクトのファイナライザーが、Javaランタイムが終了する前に実行されるように指定されます。デフォルトでは、終了時のファイナライズは無効になっています。セキュリティマネージャの場合、そのcheckExitメソッドは最初に引数として0を指定して呼び出され、終了が許可されていることを確認します。これにより、SecurityExceptionが発生する可能性があります。

4
Romain Hippeau

デッドロックがあるかどうかを確認してください。

例えば、

Runtime.getRuntime().addShutdownHook(new Thread(this::close));
System.exit(1);

そしてclose()

public void close() {
// some code
System.exit(1);
}

System.exit()は、実際にはシャットダウンフックとファイナライザーを呼び出します。したがって、シャットダウンフックが何らかの理由でexit()を内部で再度呼び出す場合(たとえば、close()がプログラムを終了する例外を発生させた場合、exit()を呼び出します。 ]そこにも)。このような..

public void close() {
  try {
  // some code that raises exception which requires to exit the program
  } catch(Exception exceptionWhichWhenOccurredShouldExitProgram) {
    log.error(exceptionWhichWhenOccurredShouldExitProgram);
    System.exit(1);
  }
}

例外をスローすることをお勧めしますが、ログに記録して終了することを選択する場合もあります。

また、デッドロックがある場合、Ctrl+Cも機能しないことに注意してください。シャットダウンフックも呼び出すので。

とにかく、それが事実である場合、問題はこの回避策によって解決することができます:

private static AtomicBoolean exitCalled=new AtomicBoolean();

    private static void exit(int status) {
        if(!exitCalled.get()) {
            exitCalled.set(true);
            System.exit(status);
        }
    }

    Runtime.getRuntime().addShutdownHook(new Thread(MyClass::close));
      exit(1);
    }

    private static void close() {
        exit(1);
    }

PS:上記のexit()バージョンは実際にはSystem.exit()メソッドでのみ記述しなければならないと感じています(JDKのPRかもしれませんか?)なぜなら、(少なくとも私からは)実質的に意味がないからです。参照)System.exit()でデッドロックを楽しませる

2
JavaTechnical

たぶん、ひどく書かれたファイナライザー?件名を読んだとき、シャットダウンフックが最初に思い浮かびました。推測:InterruptedExceptionをキャッチし、とにかく実行し続けるスレッドは、終了プロセスを保留しますか?

問題が再現可能である場合は、JVMに接続して、ハングアップしたものを示すスレッドリスト/スタックトレースを取得できるはずです。

子供がまだ本当に走っていて、それがただの未熟なゾンビプロセスではないことを確信していますか?

1
Chris Dolan

親プロセスは子プロセスからのエラーストリームと出力ストリームを消費しますか?一部のOSで、子プロセスがstdout/stderrでエラー/警告を出力し、親プロセスがストリームを消費していない場合、子プロセスはブロックされ、System.exit()に到達しません。

1

こんにちは私は同じ問題を抱えていましたが、これを無効にしたときにリモートDebugging(VmArgs:-Xdebug -Xrunjdwp:transport = dt_socket、address =%port%、server = y、suspend = y)を使用していたことが原因でしたJava.exeプロセスが期待どおりに終了しました。

1
maxbit89

明らかな原因はすべて暫定的にカバーされていると思います。例えばファイナライザー、シャットダウンフック、親プロセスの標準出力/標準エラーを正しく排出していません。何が起こっているのかを理解するには、さらに証拠が必要です。

提案:

  1. Windows XPまたはVistaマシン(または仮想))をセットアップし、関連するJREとアプリをインストールして、問題の再現を試みます。問題を再現できたら、デバッガーを接続するか、送信します。スレッドダンプを標準エラーにするための関連信号。

  2. 上記のように問題を再現できない場合は、ユーザーの1人にスレッドダンプを取得してもらい、ログファイルを転送してください。

0
Stephen C

ここで言及されていない別のケースは、シャットダウンフックが何かにぶら下がっている場合です。シャットダウンフックコードの記述には注意してください(サードパーティライブラリがぶら下がっているシャットダウンフックを登録した場合)。

ディーン

0
Dean Hiller