Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does a running thread in an object prevent it from being garbage collected in java?

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 ;)

like image 785
krosenvold Avatar asked Jan 26 '09 17:01

krosenvold


1 Answers

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.

like image 115
Lawrence Dol Avatar answered Oct 02 '22 15:10

Lawrence Dol