2017-10-07 2 views
0

Ich habe bereits im Internet gesucht, und die Frage process.waitFor() never returns zeigt an, dass es oft ein Problem mit Prozessen ist, dass ihre Stdout oder stderr nicht gelesen werden.Process.waitFor hängt, obwohl stdout und stderr lesen

Wir verwenden ProcessBuilder mit redirectOutput und redirectError dies zu erreichen, und ich denke, dass wir auf der sicheren Seite sein sollte, finden Sie in der folgenden Methode, die wir Prozesse verwenden, um auszuführen:

public static void execute(String directory, long timeout, File out, File err, String... command) throws InterruptedException, IOException { 
    LOGGER.log(Level.INFO, String.format("executing command %s (%s)", Arrays.toString(command), timeout > 0 ? String.format("timeout = %,d[ms]", timeout) : "no timeout")); 
    ProcessBuilder builder = new ProcessBuilder(); 
    builder.directory(new File(directory)); 
    builder.command(command); 
    builder.redirectOutput(out); 
    if(out == err) { 
     builder.redirectErrorStream(true);   
    } else { 
     builder.redirectError(err); 
    } 
    long time = System.currentTimeMillis(); 
    Process process = builder.start(); 
    try { 
     LOGGER.log(Level.FINE, "waiting for process"); 
     boolean exited = process.waitFor(timeout, TimeUnit.MILLISECONDS); 
     if(!exited) { 
      LOGGER.log(Level.WARNING, "timeout reached, trying to destroy ..."); 
      exited = destroy(silent, process); // Helper method to destroy processes 
     } 
     long duration = System.currentTimeMillis() - time; 
     int exitValue = process.exitValue(); 
     LOGGER.log(Level.INFO, "execution finished in " + duration + "[ms] => " + exitValue); 
    } catch (InterruptedException | Error | RuntimeException e) { 
     LOGGER.log(Level.SEVERE, "execution failed", e); 
     throw e; 
    } 
} 

Dennoch ist das Problem, dass es hängt an der process.waitFor(timeout, TimeUnit.MILLISECONDS) Anruf, obwohl der Prozess leicht innerhalb der Zeitüberschreitung beendet sein sollte.

Die Logging-Ausgabe ist

Oct 07, 2017 12:39:55 AM at.ProcessExecutor execute 
INFO: executing command [java, -Xmx8G, -XX:+UseG1GC, -XX:+CrashOnOutOfMemoryError, -jar, MetricCalc.jar] (timeout = 14,400,000[ms]) 
Oct 07, 2017 12:39:55 AM at.ProcessExecutor execute 
FINE: waiting for process 

(erkennen, dass keine execution finished Linie noch geschrieben wird)

Die err Datei liest

... Things we write to std.err ... 
Finished Metrics 

und die wichtigste Methode der MetricCalc sieht aus wie

public static void main(String[] args) { 
    .... do some stuff ... 
    System.err.println("Finished Metrics"); 
} 

was anzeigt, dass das Lesen gut funktioniert, die letzte Zeile des Java-Programms wurde ausgeführt und der Prozess sollte beendet sein.

Jeder, der eine Idee hat, warum der Prozess nicht beendet wird/hängt immer noch an Process.waitFor()?

+1

* ".... und der Prozess sollte beendet sein." * - vorausgesetzt, der Kindprozess funktioniert so, wie Sie es erwarten. Überprüfen Sie, ob der untergeordnete Prozess tatsächlich beendet wurde. z.B. mit "ps -efl" oder ähnlichem. –

+0

@StephenC: Ich kann es immer noch mit 'ps -efl | finden grep "java -Xmx8G" ', was anzeigt, dass es nicht beendet wurde. Noch habe ich keine Ahnung, warum dies der Fall ist, da die allerletzte Codezeile von MetricCalc (das Java-Programm, das als Prozess ausgeführt wird) ausgeführt wurde ... –

+1

Oh, es kam mir gerade in den Sinn: Wahrscheinlich MetricCalc Es laufen einige Nicht-Deamon-Threads, die verhindern, dass die Anwendung beendet wird ... Das muss ich testen ... –

Antwort

0

Es war kein Problem mit Process.waitFor() aber ein Problem mit der Beendigung des Prozesses.

Die Java-Anwendung, die gestartet wurde, verwendete eine ExecutorService, die nicht richtig abgeschossen wurde und Zombie-Threads am Leben ließ, die Prozessabbruch verhinderten.

Das Hinzufügen executorService.shutdown() löste das Problem, und die Anwendung wird jetzt wie erwartet beendet.