Class FinalizableReferenceQueue
- All Implemented Interfaces:
Closeable
,AutoCloseable
FinalizableReference.finalizeReferent()
on them. Java 9+ users should prefer Cleaner
; see example below.
Keep a strong reference to this object until all of the associated referents have been
finalized. If this object is garbage collected earlier, the backing thread will not invoke
finalizeReferent()
on the remaining references.
As an example of how this is used, imagine you have a class MyServer
that creates a
ServerSocket
, and you would like to ensure that the
ServerSocket
is closed even if the MyServer
object is garbage-collected without calling
its close
method. You could use a finalizer to accomplish this, but that has a
number of well-known problems. Here is how you might use this class instead:
public class MyServer implements Closeable {
private static final FinalizableReferenceQueue frq = new FinalizableReferenceQueue();
// You might also share this between several objects.
private static final Set<Reference<?>> references = Sets.newConcurrentHashSet();
// This ensures that the FinalizablePhantomReference itself is not garbage-collected.
private final ServerSocket serverSocket;
private MyServer(...) {
...
this.serverSocket = new ServerSocket(...);
...
}
public static MyServer create(...) {
MyServer myServer = new MyServer(...);
ServerSocket serverSocket = myServer.serverSocket;
Reference<?> reference = new FinalizablePhantomReference<MyServer>(myServer, frq) {
@Override
public void finalizeReferent() {
references.remove(this):
if (!serverSocket.isClosed()) {
...log a message about how nobody called close()...
try {
serverSocket.close();
} catch (IOException e) {
...
}
}
}
};
references.add(reference);
return myServer;
}
@Override
public void close() throws IOException {
serverSocket.close();
}
}
Here is how you might achieve the same thing using Cleaner
, if you are using a Java version where that is available:
public class MyServer implements Closeable {
private static final Cleaner cleaner = Cleaner.create();
// You might also share this between several objects.
private final ServerSocket serverSocket;
private final Cleaner.Cleanable cleanable;
public MyServer(...) {
...
this.serverSocket = new ServerSocket(...);
this.cleanable = cleaner.register(this, closeServerSocketRunnable(serverSocket));
...
}
private static Runnable closeServerSocketRunnable(ServerSocket serverSocket) {
return () -> {
if (!serverSocket.isClosed()) {
...log a message about how nobody called close()...
try {
serverSocket.close();
} catch (IOException e) {
...
}
}
};
}
@Override
public void close() throws IOException {
serverSocket.close();
cleanable.clean();
}
}
Some care is needed when using Cleaner
to ensure that the callback passed to
register
does not have a reference to the object (in this case, MyServer
) that may be
garbage-collected. That's why we are careful to make a Runnable
that does not have a
reference to any MyServer
instance.
- Since:
- 2.0
- Author:
- Bob Lee
-
Constructor Summary
-
Method Summary
-
Constructor Details
-
FinalizableReferenceQueue
public FinalizableReferenceQueue()Constructs a new queue.
-
-
Method Details
-
close
- Specified by:
close
in interfaceAutoCloseable
- Specified by:
close
in interfaceCloseable
-