/*
* Copyright (C) 2007 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.base;
import java.io.Closeable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.google.common.annotations.VisibleForTesting;
/**
* A reference queue with an associated background thread that dequeues
* references and invokes {@link FinalizableReference#finalizeReferent()} on
* them.
*
*
* 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 {@code
* finalizeReferent()} on the remaining references.
*
*
* As an example of how this is used, imagine you have a class {@code MyServer}
* that creates a a {@link java.net.ServerSocket ServerSocket}, and you would
* like to ensure that the {@code ServerSocket} is closed even if the
* {@code MyServer} object is garbage-collected without calling its
* {@code 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(...);
* final 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() {
* serverSocket.close();
* }
* }
*
*
* @author Bob Lee
* @since 2.0 (imported from Google Collections Library)
*/
public class FinalizableReferenceQueue implements Closeable {
/*
* The Finalizer thread keeps a phantom reference to this object. When the
* client (for example, a map built by MapMaker) no longer has a strong
* reference to this object, the garbage collector will reclaim it and enqueue
* the phantom reference. The enqueued reference will trigger the Finalizer to
* stop.
*
* If this library is loaded in the system class loader,
* FinalizableReferenceQueue can load Finalizer directly with no problems.
*
* If this library is loaded in an application class loader, it's important that
* Finalizer not have a strong reference back to the class loader. Otherwise,
* you could have a graph like this:
*
* Finalizer Thread runs instance of -> Finalizer.class loaded by -> Application
* class loader which loaded -> ReferenceMap.class which has a static ->
* FinalizableReferenceQueue instance
*
* Even if no other references to classes from the application class loader
* remain, the Finalizer thread keeps an indirect strong reference to the queue
* in ReferenceMap, which keeps the Finalizer running, and as a result, the
* application class loader can never be reclaimed.
*
* This means that dynamically loaded web applications and OSGi bundles can't be
* unloaded.
*
* If the library is loaded in an application class loader, we try to break the
* cycle by loading Finalizer in its own independent class loader:
*
* System class loader -> Application class loader -> ReferenceMap ->
* FinalizableReferenceQueue -> etc. -> Decoupled class loader -> Finalizer
*
* Now, Finalizer no longer keeps an indirect strong reference to the static
* FinalizableReferenceQueue field in ReferenceMap. The application class loader
* can be reclaimed at which point the Finalizer thread will stop and its
* decoupled class loader can also be reclaimed.
*
* If any of this fails along the way, we fall back to loading Finalizer
* directly in the application class loader.
*/
private static final Logger logger = Logger.getLogger(FinalizableReferenceQueue.class.getName());
private static final String FINALIZER_CLASS_NAME = "com.google.common.base.internal.Finalizer";
/** Reference to Finalizer.startFinalizer(). */
private static final Method startFinalizer;
static {
Class> finalizer = loadFinalizer(new SystemLoader(), new DecoupledLoader(), new DirectLoader());
startFinalizer = getStartFinalizer(finalizer);
}
/**
* The actual reference queue that our background thread will poll.
*/
final ReferenceQueue