Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can java finalize an object when it is still in scope?

I've been looking into a bug in my code that seems to be caused by some "ugly" finalizer code. The code looks roughly like this

public class A {    public B b = new B();    @Override public void finalize() {      b.close();    } }  public class B {    public void close() { /* do clean up our resources. */ }    public void doSomething() { /* do something that requires us not to be closed */ }  }  void main() {    A a = new A();    B b = a.b;    for(/*lots of time*/) {      b.doSomething();    } } 

What I think is happening is that a is getting detected as having no references after the second line of main() and getting GC'd and finalized by the finalizer thread - while the for loop is still happening, using b while a is still "in scope".

Is this plausable? Is java allowed to GC an object before it goes out of scope?

Note: I know that doing anything inside finalizers is bad. This is code I've inherited and am intending to fix - the question is whether I'm understanding the root issue correctly. If this is impossible then something more subtle must be the root of my bug.

like image 659
Michael Anderson Avatar asked Jun 24 '14 00:06

Michael Anderson


People also ask

Can an object's finalize () method be invoked while it is reachable?

Can an object's finalize() method be invoked while it is reachable? An object's finalize() method cannot be invoked by the garbage collector while the object is still reachable.

In which condition is object's Finalize?

finalize() is called by the garbage collector on an object when garbage collection determines that there are no more references to the object.

Why finalize () method should be avoided in java?

“This method is inherently unsafe. It may result in finalizers being called on live objects while other threads are concurrently manipulating those objects, resulting in erratic behavior or deadlock.” So, in one way we can not guarantee the execution and in another way we the system in danger.

Do all objects have Finalize method in java?

Since the Object class contains the finalize method hence finalize method is available for every java class since Object is the superclass of all java classes. Since it is available for every java class, Garbage Collector can call the finalize() method on any java object.


2 Answers

Can Java finalize an object when it is still in scope?

Yes.

However, I'm being pedantic here. Scope is a language concept that determines the validity of names. Whether an object can be garbage collected (and therefore finalized) depends on whether it is reachable.

The answer from ajb almost had it (+1) by citing a significant passage from the JLS. However I don't think it's directly applicable to the situation. JLS §12.6.1 also says:

A reachable object is any object that can be accessed in any potential continuing computation from any live thread.

Now consider this applied to the following code:

class A {     @Override protected void finalize() {         System.out.println(this + " was finalized!");     }      public static void main(String[] args) {         A a = new A();         System.out.println("Created " + a);         for (int i = 0; i < 1_000_000_000; i++) {             if (i % 1_000_000 == 0)                 System.gc();         }         // System.out.println(a + " was still alive.");     } } 

On JDK 8 GA, this will finalize a every single time. If you uncomment the println at the end, a will never be finalized.

With the println commented out, one can see how the reachability rule applies. When the code reaches the loop, there is no possible way that the thread can have any access to a. Thus it is unreachable and is therefore subject to finalization and garbage collection.

Note that the name a is still in scope because one can use a anywhere within the enclosing block -- in this case the main method body -- from its declaration to the end of the block. The exact scope rules are covered in JLS §6.3. But really, as you can see, scope has nothing to do with reachability or garbage collection.

To prevent the object from being garbage collected, you can store a reference to it in a static field, or if you don't want to do that, you can keep it reachable by using it later on in the same method after the time-consuming loop. It should be sufficient to call an innocuous method like toString on it.

like image 93
Stuart Marks Avatar answered Oct 18 '22 18:10

Stuart Marks


JLS §12.6.1:

Optimizing transformations of a program can be designed that reduce the number of objects that are reachable to be less than those which would naively be considered reachable. For example, a Java compiler or code generator may choose to set a variable or parameter that will no longer be used to null to cause the storage for such an object to be potentially reclaimable sooner.

So yes, I think it's allowable for a compiler to add hidden code to set a to null, thus allowing it to be garbage-collected. If this is what's happening, you may not be able to tell from the bytecode (see @user2357112's comment).

Possible (ugly) workaround: Add public static boolean alwaysFalse = false; to the main class or some other classes, and then at the end of main(), add if (alwaysFalse) System.out.println(a); or something else that references a. I don't think an optimizer can ever determine with certainty that alwaysFalse is never set (since some class could always use reflection to set it); therefore, it won't be able to tell that a is no longer needed. At the least, this kind of "workaround" could be used to determine whether this is indeed the problem.

like image 36
ajb Avatar answered Oct 18 '22 19:10

ajb