Thread-launched running processes won't destroy (Java)

19,501

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();
    }
}
}
Share:
19,501
Bastien
Author by

Bastien

Multifunction IT guy.

Updated on July 17, 2022

Comments

  • Bastien
    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
    Ali B almost 11 years
    Good 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
    Jason C almost 11 years
    It would also be useful to add some println traces to the threads to see if destroy() is actually getting reached.
  • Ali B
    Ali B almost 11 years
    I 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
    Bastien almost 11 years
    @AliB Could you try with an infinite-loop process? I only experience the issue with actually running processes.
  • Bastien
    Bastien almost 11 years
    I 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
    Jason C almost 11 years
    Ubuntu doesn't work, check out the comments I left in your question. I'm running into the same issue, randomly.
  • DannyMo
    DannyMo almost 11 years
    Why not edit this into the question rather than posting it as an answer?
  • Jason C
    Jason C almost 11 years
    I 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
    Jason C almost 11 years
    p.waitFor() waits for the process to terminate, so in the case of the InfiniteLoop, it would never return. The exec() point -- I wonder; I can find nothing in the documentation that guarantees that the process has actually been started when exec() returns, although I assume it has been because it has the ability to e.g. throw errors when the command isn't found.
  • Abhishek
    Abhishek almost 11 years
    doesnt 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
    Abhishek almost 11 years
    p.waitFor() waits for exec() to return, therefore using this as a marker..you may check whethr exec() works or not..
  • Jason C
    Jason C almost 11 years
  • Jason C
    Jason C almost 11 years
    This race condition doesn't exist here. The UNIXProcess constructor doesn't return until after the forkAndExec call in the thread completes -- that's what gate 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 call destroy() before the process is started.
  • Jason C
    Jason C almost 11 years
    The 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 calls destroy(), 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
    jtahlborn almost 11 years
    try adding p.getOutputStream().close(); p.getInputStream().close(); p.getErrorStream().close(); before the destroy call.
  • Jason C
    Jason C almost 11 years
    That 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
    Raji almost 11 years
    I 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
    Bastien almost 11 years
    Tried 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
    Raji almost 11 years
    Agreed, 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
    Raji almost 11 years
    Further 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
    Mifeet almost 9 years
    This shouldn't be necessary, if you look e.g. at the implementation of UnixProcess#destroy(), it closes all the streams automatically.
  • jtahlborn
    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
    Mifeet almost 9 years
    Right, I was looking at JDK 7 to be precise.
  • Lukas Hanacek
    Lukas Hanacek over 3 years
    The issue with java processes not throw away handle on Windows when all 3 streams are not closed manually persist even in Java 14.