close()
method being called by the user application code.
Most often, finalizers are used to solve this problem. A finalizer is created by overriding
the finalize()
method of java.lang.Object
. In that case, before
the object is garbage collected, this finalize method will be called. Unfortunately, there are severe problems
with the design of this finalizer mechanism. Using finalizers has a negative impact on the performance
of the garbage collector and can break data integrity of your application if you're not very careful
since the "finalizer" is invoked in a random thread, at a random time. If you use a lot of finalizers,
the finalizer system may be completely overwhelmed which can lead to OutOfMemoryError
s.
In addition, you have no control about when a finalizer will be run, so it can create problems with locking,
the shutdown of the JVM and other exceptional circumstances.
The solution is to eliminate finalizers where they are not strictly required and replace the necessary ones with phantom references.
Phantom references can be used to perform actions before an object is garbage collected in a safe way.
In the constructor of a java.lang.ref.PhantomReference
, you specify a
java.lang.ref.ReferenceQueue
where the phantom reference
will be enqueued once the referenced object becomes "phantom reachable".
Phantom reachable means unreachable other than through the phantom reference.
The initially confusing thing is that although the phantom reference
continues to hold the referenced object in a private field (unlike soft or weak references),
its getReference()
method always returns null
. This is so that you cannot make
the object strongly reachable again.
From time to time, you can poll the reference queue and check if there are any new phantom references
whose referenced objects have become phantom reachable.
In order to be able to to anything useful, one can for example derive a class from
java.lang.ref.PhantomReference
that references resources that should be freed before
garbage collection. The referenced object is only garbage collected once the phantom reference
becomes unreachable itself.
Let's continue with the example of the JDBC driver above: Before a connection object is garbage collected, the actual database connection must be closed. The following steps are necessary to achieve this with phantom references:
private LinkedList phantomReferences = new LinkedList();would be appropriate. This is necessary to ensure that phantom references are not garbage collected as long as they have not been handled by the reference queue.
private ReferenceQueue queue = new ReferenceQueue();
DatabaseConnection
. The
phantom reference class will thus look like:
public class ConnectionPhantomReference extends PhantomReference { private DatabaseConnection databaseConnection; public MyPhantomReference(ConnectionImpl connection, ReferenceQueue queue) { super(connection, queue); databaseConnection = connection.getDatabaseConnection(); } public void cleanup() { databaseConnection.close(); } }The custom phantom reference extracts the resource object from the implementation class of the connection and saves it in a private field. It additionally provides a
cleanup()
method that can be invoked once after the phantom reference is taken out of
the reference queue.
ConnectionPhantomReference
must be created as well and added to the phantomReferences
list:
phantomReferences.add(new ConnectionPhantomReference(connection, queue));
Thread referenceThread = new Thread() { public void run() { while (true) { try { ConnectionPhantomReference ref = (ConnectionPhantomReference)queue.remove(); ref.close(); phantomReferences.remove(ref); } catch (Exception ex) { // log exception, continue } } } }; referenceThread.setDaemon(true); referenceThread.start();The phantom reference is removed from the
phantomReferences
list. Now the
phantom reference is unreferenced itself and the referenced object can be garbage collected.