views:

662

answers:

6

Given the code:

 new Thread(new BackgroundWorker()).start();

Intuitively it feels like the BackgroundWorker instance should be safe from GC until the thread exits, but is this the case ? And why ?

Edit:

All this heat is basically generated by me asking at least two different questions in the same post. The question in the title has one answer, the code sample leads in a different direction - with two possible outcomes depending on inlining.

The answers posted are really excellent. I will be awarding Software Monkey the green checkbox. Please note that Darron's answer is equally valid, but Software Monkey explained the problem I was having; it was the answer that worked for me.

Thank you all for making this a memorable affair ;)

+1  A: 

Yes, because the Thread keeps a reference to the Runnable internally (it has to know what to run, after all).

Michael Myers
But a shallow reading might indicate that it only has to reference the runnable long enough to call it the first time.
Darron
@Darron: the first time? You can't start a thread more than once; or did I not understand your comment either?
Michael Myers
The Thread will only start once, but you can call Thread.run() extra times. That is the possibility that forces the Thread object to remember the Runnable past Thread.start().
Darron
@Darron: Ok, at first it sounded like you were saying that the Thread didn't have to keep it around.
Michael Myers
+5  A: 

Yes, it is safe. The reason why is not as obvious as you might think.

Just because code in BackgroundWorker is running does not make it safe -- the code in question may not actually reference any members of the current instance, allowing "this" to be optimized away.

However, if you carefully read the specification for the java.lang.Thread class's run() method you'll see that the Thread object must keep a reference to the Runnable in order to fulfill its contract.

EDIT: because I've been voted down several times on this answer I'm going to expand upon my explanation.

Assume for the moment that it was not legal for anyone except Thread itself to call Thread.run(),

In that case it would be legal for the default implementation of Thread.run() to look like:

void run() {
    Runnable tmp = this.myRunnable;  // Assume JIT make this a register variable.
    this.myRunnable = null;          // Release for GC.
    if (tmp != null)
        tmp.run();         // If the code inside tmp.run() overwrites the register, GC can occur.
}

What I keep saying is that nothing in the JLS prevents an object from being garbage collected just because a thread is executing an instance method. This is part of what makes getting finalization correct so hard.

For excruciating detail on this from people who understand it much better than I do, see this discussion thread from the concurrency interest list.

Darron
You must be able to read that spec better than me...
Tom Hawtin - tackline
I couldn't see it in the javadoc, maybe there's some other spec ?
krosenvold
It's pretty obvious.If this thread was constructed using a separate Runnable run object, then that Runnable object's run method is called; otherwise, this method does nothing and returns.
Craig P. Motlin
@Motlin Yes *now* I agree it's totally obvious ;)
krosenvold
And the specification doesn't prevent you from calling run() again any time you want to. So the reference has to be kept.
Darron
Calling run on the Thread object itself. Clever, but I don't think there's a happens-before there.
Tom Hawtin - tackline
@Tom You don't normally call run() yourself, or you'd be building a Thread to use it as a Runnable. Thread.start() calls Thread.run() though.
Craig P. Motlin
Yes, calling run() yourself probably counts as slightly evil. But it's public.
Darron
@Darron: See my answer - I think your assumptions are flawed.
Software Monkey
@CodeMonkey: It's not just my assumptions, it's also the reasoning of the top brains in GC. A local variable that will never be used again can be optimized away.
Darron
+1  A: 

I am willing to bet that the JVM includes a reference to each thread object that is active or can be scheduled in its root set, but I don't have the spec with me to confirm this.

Uri
Indeed it does. Classes Thread and ThreadGroup have methods that will let you get references to all currently running Threads.
Darron
The Thread object needn't keep hold a reference to the Runnable.
Tom Hawtin - tackline
@Tom -- yes, it must. Because it's legal to call Thread.run() multiple times.
Darron
+10  A: 

Yes, because GC can only collect objects not reachable by any thread, and Thread must hold a reference to it's runnable (or it would not be able to invoke it). So, clearly, your Runnable object is reachable while your thread is running.

Regardless of the semantics required for execution, your object will not be GC'd until it is no longer reachable by this new thread or any other; that will be at least long enough to invoke your Runnable's run(), and for the entire life of the thread if that thread is able to reach the Runnable instance, so your construct is guaranteed to be safe by the JVM specification.


EDIT: Because Darron is beating this to death, and some seem convinced by his argument I'm going to expand upon my explanation, based on his.

Assume for the moment that it was not legal for anyone except Thread itself to call Thread.run(),

In that case it would be legal for the default implementation of Thread.run() to look like:

void run() {
    Runnable tmp = this.myRunnable;  // Assume JIT make this a register variable.
    this.myRunnable = null;          // Release for GC.
    if(tmp != null) {
        tmp.run();         // If the code inside tmp.run() overwrites the register, GC can occur.
        }
    }

I contend that in this case tmp is still a reference to the runnable reachable by the thread executing within Thread.run() and therefore is not eligible for GC.

What if (for some inexplicable reason) the code looked like:

void run() {
    Runnable tmp = this.myRunnable;  // Assume JIT make this a register variable.
    this.myRunnable = null;          // Release for GC.
    if(tmp != null) {
        tmp.run();         // If the code inside tmp.run() overwrites the register, GC can occur.
        System.out.println("Executed runnable: "+tmp.hashCode());
        }
    }

Clearly, the instance referred to by tmp cannot be GC'd while tmp.run() is executing.

I think Darron mistakingly believes that reachable means only those references which can be found by chasing instance references starting with all Thread instances as roots, rather than being defined as a reference which can be seen by any executing thread. Either that, or I am mistaken in believing the opposite.

Further, Darron can assume that the JIT compiler makes any changes he likes - the compiler is not permitted to change the referential semantics of the executing code. If I write code that has a reachable reference, the compiler cannot optimize that reference away and cause my object to be collected while that reference is in scope.

I don't know the detail of how reachable objects are actually found; I am just extrapolating the logic which I think must hold. If my reasoning were not correct, then any object instantiated within a method and assigned only to a local variable in that method would be immediately eligible for GC - clearly this is not and can not be so.

Furthermore, the entire debate is moot. If the only reachable reference is in the Thread.run() method, because the runnable's run does not reference it's instance and no other reference to the instance exists, including the implicit this passed to the run() method (in the bytecode, not as a declared argument), then it doesn't matter whether the object instance is collected - doing so, by definition, can cause no harm since it's not needed to execute the code if the implicit this has been optimized away. That being the case, even if Darron is correct, the end practical result is that the construct postulated by the OP is perfectly safe. Either way. It doesn't matter. Let me repeat that one more time, just to be clear - in the end analysis it doesn't matter.

Software Monkey
Are you implying that the call-stacks also count as object references in GC ?
krosenvold
It's not call stacks per se. The Thread object has special behavior in the JVM.
Craig P. Motlin
Out of curiosity, what's the difference between this and my answer? And is Tom Hawtin's answer wrong?
Michael Myers
Why must the Thread object continue to hold a reference to runnable after starting?
Tom Hawtin - tackline
+1 to you mmyers. I don't know why you are downvoted. We all said the same thing. Tom is not really answering the question here.
Craig P. Motlin
@mmyers I think your answer is good. Initially I upvoted you, then all of a sudden there was doubt introduced. I removed all my votes while waiting for the public to shoot it out. You're getting my +1 back. But; this answer contains the vital extra piece of info that made it snap in place for me.
krosenvold
mmyers linked to the important part of the javadoc, which I quoted in my answer since it's so crucial. Software Monkey's answer is correct, but only implies it which is why it didn't get my vote.
Craig P. Motlin
Correct, but wrong in reasoning. Just because you call a method on an object doesn't mean that it can't be GC'ed. It depends on the code in the method. However, as stated in my answer, other things in the Thread specification make it hold a reference to the Runnable.
Darron
@Darron: I disagree - the Thread executes it's entire life inside run(); if that run() is supplied by a Runnable, then by definition the Runnable is reachable for the life time of the thread, other than perhaps minor init and cleanup inside the Thread object itself.
Software Monkey
@Software Monkey: Your last comment there is *exactly* what did it for me. It's the "(or it would not be able to invoke it)" bit :)
krosenvold
@Software Monkey: Calling a method on an object does *not* imply reachability of that object for any time beyond the time of the call. If unneeded the reference may be optimized away. There is extensive discussion on the Java memory model discussion list on this very subject.
Darron
@Darron: True, but in this case the method being invoked is the entire (relevant) life-time of the thread. So the Runnable BackgroundWorker remains safe from GC until it is no longer needed by the thread; to all intents and purposes, when the thread exits.
Software Monkey
@SoftwareMonkey: having code running in a method does *not* necessarily keep an instance from garbage collecting. If the particular method doesn't need BackgroundWorker.this for its entire duration it may be optimized away.
Darron
@Darron: In order for Thread.run() to call runnable.run(), runnable must be non-null. Since there is no construct for Thread to call runnable.run() and then make runnable null before run() returns, the reference must be held for the duration of run(), even if run() never references it's "this".
Software Monkey
@SoftwareMonkey: "there is not construct"? What about "Runnable local /* in register, overwritten quickly */ = instanceRunnable; instanceRunnable = null; local.run(); Without the documented ability to call run() extra times this code is legal.
Darron
-1 since this is the right answer for the completely wrong reason. +1 to you Darron.
Craig P. Motlin
@Darron: The Thread.run() method still holds a reference to the runnable, reachable by the thread for the period of the invocation of run() - as in local.run().
Software Monkey
@Darron: Anyway, this is all moot, practically speaking - If there is no other reference to the runnable (and there's not in the OP's construct), and the run() method does not access the object, it doesn't matter whether it's GC'd or not.
Software Monkey
@Darron: To be clear, in your example, the Thread.run method has a reference to the runnable via the variable "local"; that ref is visible to the thread for *at least* the duration of the invocation of local.run().
Software Monkey
@SoftwareMonkey It does matter if it's GC'ed. Thread.run() holds a reference to the runnable, and that's crucial to thread-safety. But what about future versions of java, will they also hold the reference? Yes, because the javadoc says so. Your answer is misleading.
Craig P. Motlin
@Motlin: Yes, that's precisely what I am saying - that Thread.run() holds a reference which prevents GC and that is precisely what my answer says. But read my comment (a completely separate thought) about this all being moot carefully - you'll realize that it is also correct.
Software Monkey
@Motlin: You are agreeing with Darron and then disagreeing in the same sentence, and doing the reverse with me. Your last comment states that I am correct, then calls my answer misleading - which is it? I say that the runnable won't be GC'd because a ref exists - Darron says not necessarily.
Software Monkey
@CodeMonkey: The variable "local" does not have to exist beyond its use. The JIT/optimizer can re-use that storage for some other variable if it can prove that local's value won't be needed again.
Darron
@CodeMonkey: we both agree that the item will not GC. We just disagree on *why* it will not GC. Which, per the discussion I reference, can be very important in finalization scenarios.
Darron
A: 

No, I don't think it is not safe.

In practice you will almost certainly get away with it. However, the Java Memory Model is surprising. Indeed there was only last week discussion on the JMM mailing list about plans to add a method to "keep objects alive". Currently the finaliser can be run without a happens-before relationship from the execution of a member method. At the moment, you technically need to introduce a happens-before realitionship by synchronising all over the place or writing some volatile at the end of each method and a read of that volatile in the finaliser.

As Darron points out, if you can get of the Thread object (through Thread.enumerate for instance) then you can call run on it, which calls the Runnable's run. However, I still don't think there is a happens-before in there.

My advice: Don't try to be too "clever".

Tom Hawtin - tackline
Wow... I was going to complain about being downvoted without a comment, but after seeing this, I guess I deserved it. :)
Michael Myers
I'm canceling out at least one of the downvotes...your first statement is wrong, for subtle reasons. The rest of your comments are absolutely correct and important.
Darron
And now you've corrected the first statement. But I suspect "happens before" doesn't matter here. Because you can get at the reference to the Runnable, there must be a strongly-reachable path to it and it can't GC.
Darron
I also smell that this answer is at a slightly deeper level than I readily comprehend. But given my revised current understanding I now think there's clear possibilities for having running threads in otherwise unreachable code, which seems like what Tom is talking about ?
krosenvold
+5  A: 

It is safe. The JVM holds onto a reference to each thread. The Thread holds on to an instance of the Runnable passed into its constructor. So the Runnable is strongly reachable, and will not be collected for the life of the Thread.

We know that the Thread holds a reference to the runnable because of the javadoc for Thread.run():

If this thread was constructed using a separate Runnable run object, then that Runnable object's run method is called; otherwise, this method does nothing and returns.

Craig P. Motlin
That does not follow.
Tom Hawtin - tackline
Anything strongly reachable will not be garbage collected. What part isn't clear?
Craig P. Motlin