0

Is it safe to reuse the same variable to create another subprocess when the previous subprocess finishes?

String cmd = nextCmd();
        while(waitForNext) {
            Process p = Runtime.getRuntime().exec(cmd);
            String iLine = null;
            String eLine = null;
            BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
            BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream()));
            while((iLine = in.readLine())!=null 
                    || (eLine = err.readLine())!=null) {
                // just to clean the Streams
            }
            p.waitFor();
            cmd = nextCmd();
        }

will it be eligible for GC'd if the loop continues for thousand times(for example)?

ron
  • 1
  • 3

2 Answers2

2

Is it safe if I use the same variable to create another subprocess if previous subprocess finishes?

Yes. It would also be safe if the subprocess weren't finished, since you're completely replacing the value stored in p with a new value (a new object reference).


In fact, your assignment in the loop you've shown isn't overwriting the previous value at all. By the time a new iteration starts, since you've declared your variable inside the block, it's either a new variable on each iteration or it's treated as though it were (you can look at it either way, probably; the bytecode probably reflects the latter but I think theory reflects the former). Your assignment is giving the (new) variable an initial value, not overwriting the previous iteration's value. If you moved your Process p declaration outside the loop, then you'd be overwriting it. Which would still be fine. :-)

That language concept doesn't necessarily translate to bytecode (which is more pragmatic), but it's true as a language concept. You could add a final modifier to p, for instance, and it would be fine. It's not the same p next time.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • One might uphold the opinion that it isn't even the same variable, from one iteration to the next. Out of scope - end of life :-) – laune Jul 26 '14 at 08:48
  • There is the JLS in 6.something talking about the scope of a block, as we have {...} here. -- Just an idea: One might try an come up with a recursive method containing such a loop with the variable declaration (of a BIG object) following the recursive call. Then, not GC-ing the variable at the end of the block would accumulate much heap space. – laune Jul 26 '14 at 10:12
  • @laune: Just for the fun of it (!), I looked at the bytecode of a simplified case (http://pastie.org/9422290, http://pastie.org/9422293). I declare a var inside a loop's block but only assign to it (`dt = new Date()`) on the first loop iteration. We see the object reference get stored in local var 2 on instruction 20 of `main`. That reference is never cleared. So if you allocate something big on an early iteration of a long loop and don't do anything with that var on subsequent iterations, it won't be eligible for GC until the end. That surprised me. Initialing with `null` fixes it, of course. – T.J. Crowder Jul 26 '14 at 10:29
  • @laune: Which kinda puts the lie to my *"In fact, your assignment in the loop you've shown isn't overwriting the previous value at all."* :-) (In purely pragmatic terms.) – T.J. Crowder Jul 26 '14 at 10:46
  • Yes, it's been quite interesting to look at these loops and their byte code. Also, you can't demonstrate that this Date variable *still* contains what was stored during the 1st iteration. When methods of the very system fail to determine the outcome of a question about that system it may be called "undecidable" ;-) – laune Jul 26 '14 at 11:45
  • My doubt is in Process class itself. First process finishes, and `p` goes out of scope(theoretically) and new instance reference is again assigned to `p`(from byte code,may be not actually a new instance), so what happens to the process underneath JVM? – ron Jul 26 '14 at 16:02
  • 1
    @T.J.Crowder *If you allocate something big on an early iteration of a long loop ... it won't be eligible for GC until the end.* Not necessarily. Don't pay too much attention to the bytecode. The language specification specification says that an object becomes unreachable when there is no longer a possibility of referencing it. The JIT compiler detects that the object becomes unreachable after the first loop iteration and can (and does, at least on Oracle JDK 8) GC it while the loop is still running. See [this answer](http://stackoverflow.com/a/24380219/1441122). – Stuart Marks Jul 26 '14 at 17:30
  • @StuartMarks: Interesting. I don't see *how* it can know, from the bytecode. There's nothing saying "this variable's scope starts here and ends here" that I can see. Perfectly willing to believe, but I'd like to know how it can know. – T.J. Crowder Jul 26 '14 at 17:59
  • @T.J.Crowder I'm not a compiler writer myself, but the concepts aren't terribly difficult. The main observation is that the `dt` variable (or local var 2) is never used after it's written. Since it's never used, why bother storing it at all? Thus the constructed object is immediately unreachable and can be GC'd at the JVM's convenience. It may be possible to elide the constructor call entirely, but probably not, since constructors might have side effects. – Stuart Marks Jul 26 '14 at 21:19
  • @StuartMarks: Yes, I understand the concept of reachability. Your example is definitely interesting, but it relates to object cleanup, not variables. My point above for the OP is that in pragmatic bytecode terms, their *variable* is reused. But I am interested by the fact that although an object is referenced by a variable that is "in scope," the JVM and/or JIT can still reclaim the object. It's also fun to test the limits. [This does it](http://pastie.org/9424173) for instance, but [this doesn't](http://pastie.org/9424175). :-) – T.J. Crowder Jul 27 '14 at 08:07
  • 1
    Your strikeout text is actually correct. You basically have a new variable `p` in every iteration. That’s a language concept, regardless of the bytecode. A consequence of that is, that you could add a `final` modifier to the variable without getting an error and therefore, it would also be legal to capture its value in an inner class or lambda expression. Placing the variable outside the loop, while generating the same bytecode, would not allow this anymore. The actual `Process` instances on the other hand, are entirely unaffected by the variables, as Stuart Marks already pointed out. – Holger Nov 21 '19 at 14:08
  • @Holger - Thanks. I'm not sure why I went down a bytecode rabbit hole there. I should know better... – T.J. Crowder Nov 21 '19 at 14:41
1

In a loop like this:

   while(waitForNext) {
        Process p = Runtime.getRuntime().exec(cmd);
        // ...
        p.waitFor();
        cmd = nextCmd();
    }

you are not "reusing a variable" (p). The variable goes out of scope when you leave the block. Reentering it, creates a new variable.

This is reusing variable p, and it's safe according to your question.

Process p = Runtime.getRuntime().exec(cmd);
// ...
p.waitFor();
p = Runtime.getRuntime().exec(anotherCmd);
// ...
p.waitFor();

Later

Due to the byte code T.J. Crowder has studiously added to his answer, one must say that the issue of this being or not being "the same variable" is (as I'd like to put it:) undecidable. Byte code shows that the variable is not "destroyed" at the end of the loop body, but you can't make use of the value from the previous iteration anyway, since not initialising and trying to aaccess the variable in the 2nd iteration is blocked by the compiler.

laune
  • 31,114
  • 3
  • 29
  • 42
  • will the instance of process be eligible for garbage collection as well? – ron Jul 26 '14 at 09:44
  • 1
    Certainly, unless a copy of the reference to the new Process object has been stored elsewhere (which isn't in your code: so definitely no). – laune Jul 26 '14 at 09:52
  • *"The variable goes out of scope when you leave the block. Reentering it, creates a new variable."* That may or may not be true in theory (I'd have to dig through the JLS), but it would appear *not* to be true in pragmatic JVM/bytecode terms (see my answer for details). – T.J. Crowder Jul 26 '14 at 10:42
  • Variable scopes are a source code artifact. After compiling to bytecode, there is no variable scope anymore, hence, you can’t reason about variable scopes by looking at the bytecode. Placing the variable outside the loop may produce exactly the same bytecode and still have a different semantics. See also [this comment on the other answer](https://stackoverflow.com/questions/24968950/java-is-it-safe-to-use-same-subprocess-variable-after-the-subprocess-finishes#comment104206719_24968975). – Holger Nov 21 '19 at 14:13