Discovering Objects with Non-trivial Finalizers

by Dr. Heinz M. Kabutz

Abstract:It is well known that implementing a non-trivial finalize() method can cause GC and performance issues, plus some subtle concurrency bugs. In this newsletter, we show how we can find all objects with a non-trivial finalize() method, even if they are not currently eligible for finalization.

Welcome to the 170th issue of The Java(tm) Specialists' Newsletter, sent from the
beautiful island of Crete. A few weeks ago,
Helene's Mac Mini hard disk packed up. Opening the Mac Mini
voids the warranty, but I had no choice as the support in
Greece is terrible. So after some handy work with spatulas
and screw drivers, the drive was replaced with a larger and
faster specimen. Next I booted the Mac OS X DVD and
clicked on "Restore from Backup". A few hours later,
everything, and I mean everything was back. User
accounts, emails, settings, programs. Very impressive indeed.

We intend being in Chania (Crete) during August this year,
so please let me know a while in advance if you are planning
a visit to our island this season. We have some nice local
restaurants, where they serve delicious Cretan specialities.

Discovering Objects with Non-trivial Finalizers

We should all know by now that implementing the finalize()
method is usually a bad idea. For reasons why, have a look at
Joshua Bloch's book on Effective
Java, Brian Goetz's book on
Java Concurrency in Practice and
Jack Shirazi's book on Java
Performance Tuning. Then there are several
articles and presentations. For example, Jack Shirazi again
explaining Finalizers part
1 and part
2. A talk about Finalizers at Java
One by Hans Boehm and lastly a nice article by Tony
Printezis. I have no doubt left out many relevant
articles, but you probably got the point: Avoid Finalizers.

The one place where I have implemented the finalize() method
was when I wanted to make sure that a resource was
being closed. The finalize() method would then check that
I really had done so. Fortunately, when our finalize()
method is trivial, that is, it has an empty body, it is
ignored by the Finalizer mechanism. We can define a
private static final boolean field
that we use to conditionally remove the body of the
finalize() method. Thus when the field is
set to false, the static compiler
removes the body of the if() statement from the compiled byte
code. Since it then contains only a trivial finalize()
method, it is not marked for finalization.

Consider the class ConditionalFinalizer.
It represents a resource that ought to be closed. We can
use the finalizer to look for situations where the client
code did not call the close() method:

We can test this once with DEBUG set to true
and then again set to false. We
should see dramatic differences in performance. On my
machine it takes about 18 seconds with a non-trivial
finalize() method, that is when DEBUG=true, but only 300
milliseconds when DEBUG=false. Here is the test code:

Have a look at Jack
Shirazi's article for reasons why we have such a
dramatic difference in performance. Finalizers introduce an
extra step in the GC, so objects end up in the old generation
unnecessarily.

The real cost of the finalize() method is that there are
handles to all our objects. This causes them to survive too
many collections, thus making them get promoted prematurely
to the old generation. The continuous old generation GC is
what makes it so slow. In our tests, we found that sometimes
80% of CPU was spent in GC. We could improve the situation by
setting the various generation size ratios, but the cost was
still substantial. With some VMs we even got an
OutOfErrorMemory as the Finalizer could not keep up with the
object creation rate.

Memory Overhead

On a 64-bit machine every object uses at least 16 bytes of
memory. A Boolean instance uses 24 bytes, so 192 bits to
represent a single bit of information! However, if we make
the class implement a non-trivial finalize() method, then it
also creates a java.lang.ref.Finalizer instance (16 bytes),
which contains a next pointer (8 bytes) and a prev pointer
(8 bytes) to represent a linked list. Since Finalizer
extends Reference, it also contains a pointer to the
referent (8 bytes), a pointer to the reference queue (8
bytes), another next pointer (8 bytes) and a "discovered"
pointer (8 bytes). This all adds up to 64 additional bytes
for each instance created with a non-trivial finalize()
method on a 64-bit machine, so each such object uses 80 bytes
at least!

Finding Finalizeable Objects

I hope it is clear that Finalization is something we want to
avoid if possible. So what do we do if we have a system and
we suspect that there are many objects with a non-trivial
finalize() method? How do we find out what these objects
are? How do we find out what classes they belong to? The
java.lang.ref.Finalizer class contains a linked list, where
the head is referenced by the unfinalized field.

All access to this linked list is synchronized with a static
lock, thus adding a point of contention to the system. If we
want to iterate over this list and print out the objects, we
should first synchronize on the same lock.

To access this information, we will define an MBean that
prints all objects with a non-trivial finalize() method. When
detailed is true, it prints out all
the field values of the objects. We can furthermore force
the finalizers to run in a separate thread to the default
Finalizer thread, with runFinalizers(). This could be
necessary if the Finalizer thread gets stuck processing a
finalize() method. Also, we offer the System.gc() method
via the MBean interface:

We have the following implementation, using reflection to
glean the correct information from the Finalizer class. It
is fairly simple, all I needed to find out was where to look
for the information. We use a simple visitor to walk over
the linked list. Various methods then implement their own
visitors to process the elements. This way we only have to
write the iterating code once.

We could thus very easily spot that we have too many objects
with finalizers. In my FinalizerWatcher, I also offer the
function to print all the objects with their fields to the
console. This would typically only be useful when you don't
have too many objects in the list. For example:

We can now see all the objects that are registered to one day
be finalized. I've tried to make the FinalizerWatcher
lightweight enough that it should still work in a busy
system. However, in a stressed system, we might not be able
to attach JConsole, in which case, just write a
java.util.Timer that calls the
printUniqueClassesWithFinalizers() periodically.

Oracle and Java are registered trademarks of Oracle and/or its
affiliates. Other names may be trademarks of their respective
owners. JavaSpecialists.eu is not connected to Oracle, Inc.
and is not sponsored by Oracle, Inc.