/* * 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