Finalize() Called on Strongly Reachable Objects in Java 8

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.

Is 'finalize()' method always called by garbage collector before destroying an 'Unreachable' object?

When the GC has found an object with class where finalize() has been overridden it is added to a queue of objects to have finalize() called on them. It is only after the object has been finalized once, that the GC can clean it up. i.e. this would be on a later GC.

e.g. If an object is in tenured space, it might be found until a full collection is performed, and it will only be cleaned up on a full GC after the finalize method has been called.

For further details, this is the Java 11 Javadoc for Object.finalize()

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html#finalize()

so is there any possibility that garbage collector fully destroyed the object in heap but didn't call the finalize() method on that object?

While the object is in the finalization queue, it can't be removed.

And you all assumed that there is not certainity in the destroying of object by the garbage collector.

It won't be destroyed while there is still a strong reference to it.

Optimal way to use phantom references in Java 8, or use weak references instead?

Weak and phantom references are indeed equivalent when no finalization is involved. However, a common misconception is to assume that an object is only subject to finalizer reachability and potential resurrection when its own class has a finalize() method.

To demonstrate the behavior, we may use

Object o = new Object();
ReferenceQueue<Object> q = new ReferenceQueue<>();
Reference<?> weak = new WeakReference<>(o, q), phantom = new PhantomReference<>(o, q), r;
// ...
o = null;
for(int cycles = 0, got = 0; got < 2; ) {
while((r = q.remove(100)) == null) {
System.gc();
cycles++;
}
got++;
System.out.println(
(r == weak? "weak": r == phantom? "phantom": "magic unicorn")
+ " ref queued after " + cycles + " cycles");
}

This typically prints either,

phantom ref queued after 1 cycles
weak ref queued after 1 cycles

or

weak ref queued after 1 cycles
phantom ref queued after 1 cycles

as both references are truly treated the same in this case and there’s no preferred order when both are enqueued in the same garbage collection.

But when we replace the // ... line with

class Legacy {
private Object finalizerReachable;

Legacy(Object o) {
finalizerReachable = o;
}
@Override
protected void finalize() throws Throwable {
System.out.println("Legacy.finalize()");
}
}
new Legacy(o);

The output changes to something like

Legacy.finalize()
weak ref queued after 1 cycles
phantom ref queued after 2 cycles

as Legacy’s finalizer is enough to make the the object finalizer reachable and open the possibility to resurrect the object during finalization.

This doesn’t have to stop you from using this approach. You may decide that there’s no such finalizer in your entire application or accept this scenario as known limitation, to only apply if someone intentionally adds such a finalizer. JDK 18 has marked the finalize() method as deprecated, for removal, so this issue will disappear in the future without requiring you to take any action.


Still, your other approach using a dummy object with a PhantomReference will work as intended, having the phantom reference only enqueued when the dummy object and hence, also the outer object, is not even finalizer reachable anymore. The drawback is the (very) slightly higher memory consumption due to the additional dummy object.

Mind that the markUsed() method may set dummyObject to null to.


Another possible point of view is that when your feature is intended to log a wrong usage of your class, which should normally not happen, it doesn’t matter when it might temporarily consume more memory when it happens. When markUsed() has been called, the phantom reference is cleared and left to garbage collection without getting enqueued, so in the case of a correct usage, the memory is not held longer than necessary.

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.

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.

What will happen when finilize method set object's reference to object property

The line you added

this.a=this;

is not something that will prevent A from being GC, this object is still not being referenced from something valid like a live thread.

Try looking at a more complex structure: List

if you point the last node to the first (circular list), and then to set your Node head = null; then maybe each node is still being pointed from the other node but the whole List is not being referenced from a live thread and there for will be garbage collected.

Garbage collector is not just checking if the object is being referenced, but deep checking if there is a reference from a valid thread.

Bottom line is:

If an object is unreachable from a thread it's garbage collected. In your case A is not reachable any more.

Why is closing resources important when you have finalize()

The assumption that “you have the finalize() block that should close all native resources when the GC gets to it” is wrong in the first place. There is no guaranty that every object representing a native resource has such a finalize() method.

Second, a resource isn’t necessarily a native resource.

When you have, e.g. a BufferedOutputStream, an ObjectOutputStream, or a ZipOutputStream wrapping a FileOutputStream, the latter likely has a finalize() method to free the underlying native resource (that’s implementation dependent), but that doesn’t write any pending data of the wrapper stream needed to have correctly written data. Closing the wrapper stream is mandatory to ensure that the written output is complete.

Naively adding a finalize() method to these wrapper stream classes to close them would not work. When the stream object gets collected, it implies that there is no reference to it, in other words there is no directed application→wrapper stream→native stream graph anymore. Since object graphs could be cyclic, there is no guaranty that an expensive search for an order among dead objects would succeed, and that’s why the JVM doesn’t even try.

Or, as the specification puts it:

The Java programming language imposes no ordering on finalize method calls. Finalizers may be called in any order, or even concurrently.

Therefore, a finalize() method of a wrapper would not be guaranteed to be called before the finalize() method of underlying native stream, thus, the underlying native stream might have been finalized and closed before the wrapper stream making it impossible to write the pending data.

And the rabbit hole goes even deeper. Object instances are maintained by the JVM as needed by the code, but the code can get optimized to use encapsulated data directly. If the wrapper stream class had a finalize() method, you might find out that the wrapper instance can be freed even earlier than expected, as discussed in finalize() called on strongly reachable object in Java 8.

Or, in short, explicit closing is the only way to ensure that it happens exactly at the right point of time.



Related Topics



Leave a reply



Submit