Thread-launched running processes won't destroy (Java)
Solution 1
Use a p.waitFor();
before p.destroy();
,
this will ensure the completion of the previous process. I think you p.destroy command gets invoked sooner than the exec()
command performs the action. Therefore it becomes useless.
Solution 2
This is simply because before the threads execute the destroy call, your main program terminates and all the associated threads leaving the started processes running. To verify this, simply add a System.out call after the destroy and you will find it is not executed. To overcome this add a Thread.sleep at the end of your main method and you will not have the orphaned processes. The below does not leave any process running.
public class ProcessTest {
public static final void main (String[] args) throws Exception {
for(int i = 0; i < 100; i++) {
new Thread(new Runnable()
{
public void run() {
try {
Process p = Runtime.getRuntime().exec(new String[]{"java", "InfiniteLoop"});
Thread.sleep(1);
p.destroy();
System.out.println("Destroyed");
}catch(IOException e) {
System.err.println("exception: " + e.getMessage());
} catch(InterruptedException e){
System.err.println("exception: " + e.getMessage());
}
}
}).start();
}
Thread.sleep(1000);
}
}
Solution 3
You should close the input/output/error streams to the process. We saw some issues in the past where the forked process was not completing properly due to those streams not being closed (even if they weren't being used).
An exemplary solution:
p.destroy();
p.getInputStream().close();
p.getOutputStream().close();
p.getErrorStream().close();
Solution 4
I believe that according to link, a distinct process is spawned by the operating system in response to this call. This process has a lifetime independent of your Java program and threads within it so you would expect it to continue running after your program has exited. I just tried it on my machine and it appeared to work as expected:
import java.io.*;
class Mp {
public static void main(String []args) {
for(int i = 0; i < 100; i++) {
new Thread(new Runnable() {
public void run() {
try {
System.out.println("1");
Process p = Runtime.getRuntime().exec
(new String[]{"notepad", ""});
System.out.println("2");
Thread.sleep(5);
System.out.println("3");
p.destroy();
System.out.println("4");
}
catch(IOException | InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
Comments
-
Bastien almost 2 years
Starting multiple threads and having each exec() then destroy() a running java process result in some of the process not being destroyed and still running after program exit. Here is some code that reproduce the issue. I noticed the more threads you start, the more processes stay alive. And the more sleep before destroy(), the less processes stay alive. (I used InfiniteLoop as an example. Any running process will do the trick.)
EDIT : Bug has been reported to Oracle, waiting for an answer. Feel free to share any knowledge/experiments on the subject.
for(int i = 0; i < 100; i++) { new Thread(new Runnable() { public void run() { try { Process p = Runtime.getRuntime().exec(new String[]{"java", "InfiniteLoop"}); Thread.sleep(1); p.destroy(); }catch(IOException | InterruptedException e){e.printStackTrace();} } }).start(); }
-
Ali B almost 11 yearsGood point - I forgot about the destroy call. It would be useful to add a e.printStackTrace() to the exception block to see if any errors are occurring.
-
Jason C almost 11 yearsIt would also be useful to add some println traces to the threads to see if destroy() is actually getting reached.
-
Ali B almost 11 yearsI tried it out on my machine just now as follows with no luck - I wonder if the JVM instance on the OP's machine has an issue.
-
Bastien almost 11 years@AliB Could you try with an infinite-loop process? I only experience the issue with actually running processes.
-
Bastien almost 11 yearsI still experience the issue with this code. I tried on a different machine with Fedora, same problem. But I tried on a Windows 7 64 in virtual box and there was no problem! Is it OS related then?
-
Jason C almost 11 yearsUbuntu doesn't work, check out the comments I left in your question. I'm running into the same issue, randomly.
-
DannyMo almost 11 yearsWhy not edit this into the question rather than posting it as an answer?
-
Jason C almost 11 yearsI needed a place to post my test code when I was going back and forth with the OP. I don't feel it is appropriate to edit it into somebody else's question. I will remove this once it is resolved. I agree it is not ideal but SO does not lend itself well to these types of discussions. :)
-
Jason C almost 11 years
p.waitFor()
waits for the process to terminate, so in the case of theInfiniteLoop
, it would never return. Theexec()
point -- I wonder; I can find nothing in the documentation that guarantees that the process has actually been started whenexec()
returns, although I assume it has been because it has the ability to e.g. throw errors when the command isn't found. -
Abhishek almost 11 yearsdoesnt it wait for the current process to return? and then p.destroy kills the process..it completes one creation and destruction cycle in one iteration..
-
Abhishek almost 11 yearsp.waitFor() waits for exec() to return, therefore using this as a marker..you may check whethr exec() works or not..
-
Jason C almost 11 years
-
Jason C almost 11 yearsThis race condition doesn't exist here. The
UNIXProcess
constructor doesn't return until after theforkAndExec
call in the thread completes -- that's whatgate
is for. In fact, that's how it is able to throw an exception if the process fails to start, you'll note that exception originates from that background thread. So the constructor will not return until the process is started, and therefore it is not possible to calldestroy()
before the process is started. -
Jason C almost 11 yearsThe comment you see in
destroy()
is referring to a different scenario. That comment refers to the fact that if a process dies on its own, the OS may reuse its PID for some other new process, so by the time our application callsdestroy()
, the PID may be a different unrelated process that we don't want to kill. The code near that comment just says "if the process already exited on its own, don't kill it", which is an attempt (not 100% reliable, as noted) to avoid killing the wrong thing. -
jtahlborn almost 11 yearstry adding
p.getOutputStream().close(); p.getInputStream().close(); p.getErrorStream().close();
before the destroy call. -
Jason C almost 11 yearsThat doesn't resolve the issue; that's actually one of the first tests I ran too. I had a 10 second sleep, and during that time, a handful of the processes remained running during the entire sleep even though they should've been destroyed (and didn't terminate when the program terminated either).
-
Raji almost 11 yearsI am running the above code in Debian and it has no processes left after adding Thread.sleep(). What I have done above is not the correct way to wait for threads to stop. You will have to call Thread.join on each of the threads created in a loop at the end. Have you tried this and is it still the same problem?
-
Bastien almost 11 yearsTried your code, problem remains. But I don't see your point, the problem does not come from the threads but from the processes. A terminating thread does not terminate the processes it launched.
-
Raji almost 11 yearsAgreed, the started processes are not terminating. But there are two reasons for not terminating, it is not terminating because destroy is not called or destroy itself has a bug. To eliminate the first, I suggested that you join the main thread with the started threads, so you are sure that the threads have terminated which eliminates that scenario. Calling "sleep" method does not eliminate any scenario.
-
Raji almost 11 yearsFurther researching shows the following results: on Debian "jre build 1.7.0-b147" / "jdk build 1.6.0_31-b04" and on windows 7 32-bit "java build 1.6.0_31-b04", the processes are getting terminated correctly. But on a windows 7 64-bit using a jdk build 1.7.0-b147, the processes are not getting terminated even if destroy is called.
-
Mifeet almost 9 yearsThis shouldn't be necessary, if you look e.g. at the implementation of
UnixProcess#destroy()
, it closes all the streams automatically. -
jtahlborn almost 9 years@Mifeet - this question was answered 2 years ago. are you looking at a production version of java from two years ago? (and this experience may be from early jdk 6 or even 5, tough to keep them all straight at this point in time).
-
Mifeet almost 9 yearsRight, I was looking at JDK 7 to be precise.
-
Lukas Hanacek over 3 yearsThe issue with java processes not throw away handle on Windows when all 3 streams are not closed manually persist even in Java 14.