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
How to Check If a File Exists in Java
How to Parse/Format Dates With Localdatetime - Java 8
How to List the Files Inside a Jar File
How Many Ways to Click on Webelement in Webdriver
Converting 'Arraylist≪String≫ to 'String[]' in Java
Junit Test For System.Out.Println()
How to Convert Java.Util.Date to Java.Sql.Date
How to Get X and Y Index of Element Inside Gridlayout
What Are the Effects of Exceptions on Performance in Java
How to Run Unix Shell Script from Java Code
Adding External Library to Artifact Jar in Intellij Idea
How to Implement Rest Token-Based Authentication With Jax-Rs and Jersey
Difference Between Applicationcontext.Xml and Spring-Servlet.Xml in Spring Framework
How to Convert Int[] into List≪Integer≫ in Java
How to Split a String With Any Whitespace Chars as Delimiters