Can Java Finalize an Object When It Is Still in Scope

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

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.

When is the finalize() method called in Java?

In general it's best not to rely on finalize() to do any cleaning up etc.

According to the Javadoc (which it would be worth reading), it is:

Called by the garbage collector on an object when garbage collection determines that there are no more references to the object.

As Joachim pointed out, this may never happen in the life of a program if the object is always accessible.

Also, the garbage collector is not guaranteed to run at any specific time. In general, what I'm trying to say is finalize() is probably not the best method to use in general unless there's something specific you need it for.

Does JVM garbage collect objects being referenced by local variables which are no longer used?

As elaborated in Can java finalize an object when it is still in scope?, local variables do not prevent the garbage collection of referenced objects. Or, as this answer puts it, scope is a only a language concept, irrelevant to the garbage collector.

I’ll cite the relevant part of the specification, JLS §12.6.1 again:

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

Further, I extended the answer’s example to

class A {
static volatile boolean finalized;

Object b = new Object() {
@Override protected void finalize() {
System.out.println(this + " was finalized!");
finalized = true;
}
@Override public String toString() {
return "B@"+Integer.toHexString(hashCode());
}
};
@Override protected void finalize() {
System.out.println(this + " was finalized!");
}

@Override public String toString() {
return super.toString() + " with "+b;
}

public static void main(String[] args) {
A a = new A();
System.out.println("Created " + a);
for(int i = 0; !finalized; i++) {
if (i % 1_000_000 == 0)
System.gc();
}
System.out.println("finalized");
}
}
Created A@59a6e353 with B@6aaa5eb0
B@6aaa5eb0 was finalized!
finalized
A@59a6e353 with B@6aaa5eb0 was finalized!

which demonstrates that even the method with the variable in scope may detect the finalization of the referenced object. Further, being referenced from a heap variable doesn’t necessarily prevent the garbage collection either, as the B object is unreachable, as no continuing computation can access it when the object containing the reference is unreachable too.


It’s worth emphasizing that even using the object does not always prevent its garbage collection. What matters, is whether the object’s memory is needed for the ongoing operation(s) and not every access to an object’s field in source code has to lead to an actual memory access at runtime. The specification states:

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. […]

Another example of this occurs if the values in an object's fields are stored in registers. The program may then access the registers instead of the object, and never access the object again. This would imply that the object is garbage.

This is not only a theoretical option. As discussed in finalize() called on strongly reachable object in Java 8, it may even happen to objects while a method is invoked on them, or in other words, the this reference may get garbage collected while an instance method is still executing.

The only ways to prevent an objects garbage collection for sure, are synchronization on the object if the finalizer also does synchronization on the object or calling Reference.reachabilityFence(object), a method added in Java 9. The late addition of the fence method demonstrates the impact of the optimizers getting better from version to version on the issue of earlier-than-wanted garbage collection. Of course, the preferred solution is to write code that does not depend on the time of garbage collection at all.

Can Java garbage collect variables before end of scope?

I would say the object is collectable at point 2, going by the following language in JLS section 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.

Since the bt variable will no longer be used after point 2, Java is free to clear that variable, rendering the BigThing object only weakly reachable.

Java scopes inside a scope

As the Java Language Specification states

The scope of a declaration is the region of the program within which
the entity declared by the declaration can be referred to using a
simple name, provided it is visible (§6.4.1).

In other words, scope only regulates the use of names within source code. It has no effect on runtime behavior, and therefore has no effect on garbage collection.

Your first suggestion is more or less the only reason to use a block. But some times it gets too cumbersome. Personally, I've maybe used them once or twice in code I've written.


Garbage collection is partly defined by finalization of class instances. That chapter speaks about reachability

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

The JVM cannot collect such objects.

The JLS also mentions

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.

This is also discussed in the answer to

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

GC not clearing objects in scope

I can’t reproduce your result with -Xmx500m, but with -Xmx200m. Consistently, the program will fail when you add the finalize() method, as the finalizer is the cause of the problem. However, there are some "finalized" message printed in my system. Without the finalize() method, it runs perfectly forever (well, actually until I kill it).

There is no problem reclaiming the space of ordinary objects, but when you add a nontrivial finalize() method, you are actively preventing the objects from going out of scope, as they now must be enqueued for finalization.

While the JVM will always perform a garbage collection, trying to free all unused objects, before throwing an OutOfMemoryError, there is no such guaranty for objects hanging in the finalization queue. Slowing down the main task by disabling the JIT may indeed allow the finalization to process more elements. Keep in mind that having both, the main task and the finalize method printing to System.out has a synchronization effect which can make the timing of the processing critical.

The are several environmental aspects that can influence the outcome. Since there is no guaranty regarding which thread will execute a finalizer, the finalization can use all unused CPU cores (and your main thread uses only one).
Also the actual garbage collection algorithm (the JVM allows selecting one of several different algorithms) can have an impact.

Java object is gc-ed when it's still linked with a strong reference and a weaked reference

Since your loop is running quite "hot" in terms of resource usage, the JVM will want to optimize it. Doing so might point the compiler to the fact that in fact the strong reference can't be reached after entering the loop and thus optimize it away.

This behaviour will likely change if you add something like System.out.println(obj) after your loop. You might need to tweak your loop condition so that the compiler can't see that the new statement is in fact unreachable code... (something like while (weakRef.get() != null))

finalize() called on strongly reachable objects in Java 8

A bit of conjecture here. It is possible for an object to be finalized and garbage collected even if there are references to it in local variables on the stack, and even if there is an active call to an instance method of that object on the stack! The requirement is that the object be unreachable. Even if it's on the stack, if no subsequent code touches that reference, it's potentially unreachable.

See this other answer for an example of how an object can be GC'ed while a local variable referencing it is still in scope.

Here's an example of how an object can be finalized while an instance method call is active:

class FinalizeThis {
protected void finalize() {
System.out.println("finalized!");
}

void loop() {
System.out.println("loop() called");
for (int i = 0; i < 1_000_000_000; i++) {
if (i % 1_000_000 == 0)
System.gc();
}
System.out.println("loop() returns");
}

public static void main(String[] args) {
new FinalizeThis().loop();
}
}

While the loop() method is active, there is no possibility of any code doing anything with the reference to the FinalizeThis object, so it's unreachable. And therefore it can be finalized and GC'ed. On JDK 8 GA, this prints the following:

loop() called
finalized!
loop() returns

every time.

Something similar might be going on with MimeBodyPart. Is it being stored in a local variable? (It seems so, since the code seems to adhere to a convention that fields are named with an m_ prefix.)

UPDATE

In the comments, the OP suggested making the following change:

    public static void main(String[] args) {
FinalizeThis finalizeThis = new FinalizeThis();
finalizeThis.loop();
}

With this change he didn't observe finalization, and neither do I. However, if this further change is made:

    public static void main(String[] args) {
FinalizeThis finalizeThis = new FinalizeThis();
for (int i = 0; i < 1_000_000; i++)
Thread.yield();
finalizeThis.loop();
}

finalization once again occurs. I suspect the reason is that without the loop, the main() method is interpreted, not compiled. The interpreter is probably less aggressive about reachability analysis. With the yield loop in place, the main() method gets compiled, and the JIT compiler detects that finalizeThis has become unreachable while the loop() method is executing.

Another way of triggering this behavior is to use the -Xcomp option to the JVM, which forces methods to be JIT-compiled before execution. I wouldn't run an entire application this way -- JIT-compiling everything can be quite slow and take lots of space -- but it's useful for flushing out cases like this in little test programs, instead of tinkering with loops.



Related Topics



Leave a reply



Submit