| /* |
| * 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 com.google.common.annotations.VisibleForTesting; |
| |
| 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; |
| |
| /** |
| * A reference queue with an associated background thread that dequeues references and invokes |
| * {@link FinalizableReference#finalizeReferent()} on them. |
| * |
| * <p>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. |
| * |
| * <p>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 <em>could</em> use a finalizer to accomplish this, but |
| * that has a number of well-known problems. Here is how you might use this class instead: |
| * |
| * <pre> |
| * 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(); |
| * } |
| * } |
| * </pre> |
| * |
| * @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<Object> queue; |
| |
| final PhantomReference<Object> frqRef; |
| |
| /** |
| * Whether or not the background thread started successfully. |
| */ |
| final boolean threadStarted; |
| |
| /** |
| * Constructs a new queue. |
| */ |
| public FinalizableReferenceQueue() { |
| // We could start the finalizer lazily, but I'd rather it blow up early. |
| queue = new ReferenceQueue<Object>(); |
| frqRef = new PhantomReference<Object>(this, queue); |
| boolean threadStarted = false; |
| try { |
| startFinalizer.invoke(null, FinalizableReference.class, queue, frqRef); |
| threadStarted = true; |
| } catch (IllegalAccessException impossible) { |
| throw new AssertionError(impossible); // startFinalizer() is public |
| } catch (Throwable t) { |
| logger.log(Level.INFO, "Failed to start reference finalizer thread." |
| + " Reference cleanup will only occur when new references are created.", t); |
| } |
| |
| this.threadStarted = threadStarted; |
| } |
| |
| @Override |
| public void close() { |
| frqRef.enqueue(); |
| cleanUp(); |
| } |
| |
| /** |
| * Repeatedly dequeues references from the queue and invokes {@link |
| * FinalizableReference#finalizeReferent()} on them until the queue is empty. This method is a |
| * no-op if the background thread was created successfully. |
| */ |
| void cleanUp() { |
| if (threadStarted) { |
| return; |
| } |
| |
| Reference<?> reference; |
| while ((reference = queue.poll()) != null) { |
| /* |
| * This is for the benefit of phantom references. Weak and soft references will have already |
| * been cleared by this point. |
| */ |
| reference.clear(); |
| try { |
| ((FinalizableReference) reference).finalizeReferent(); |
| } catch (Throwable t) { |
| logger.log(Level.SEVERE, "Error cleaning up after reference.", t); |
| } |
| } |
| } |
| |
| /** |
| * Iterates through the given loaders until it finds one that can load Finalizer. |
| * |
| * @return Finalizer.class |
| */ |
| private static Class<?> loadFinalizer(FinalizerLoader... loaders) { |
| for (FinalizerLoader loader : loaders) { |
| Class<?> finalizer = loader.loadFinalizer(); |
| if (finalizer != null) { |
| return finalizer; |
| } |
| } |
| |
| throw new AssertionError(); |
| } |
| |
| /** |
| * Loads Finalizer.class. |
| */ |
| interface FinalizerLoader { |
| |
| /** |
| * Returns Finalizer.class or null if this loader shouldn't or can't load it. |
| * |
| * @throws SecurityException if we don't have the appropriate privileges |
| */ |
| Class<?> loadFinalizer(); |
| } |
| |
| /** |
| * Tries to load Finalizer from the system class loader. If Finalizer is in the system class path, |
| * we needn't create a separate loader. |
| */ |
| static class SystemLoader implements FinalizerLoader { |
| // This is used by the ClassLoader-leak test in FinalizableReferenceQueueTest to disable |
| // finding Finalizer on the system class path even if it is there. |
| @VisibleForTesting |
| static boolean disabled; |
| |
| @Override |
| public Class<?> loadFinalizer() { |
| if (disabled) { |
| return null; |
| } |
| ClassLoader systemLoader; |
| try { |
| systemLoader = ClassLoader.getSystemClassLoader(); |
| } catch (SecurityException e) { |
| logger.info("Not allowed to access system class loader."); |
| return null; |
| } |
| if (systemLoader != null) { |
| try { |
| return systemLoader.loadClass(FINALIZER_CLASS_NAME); |
| } catch (ClassNotFoundException e) { |
| // Ignore. Finalizer is simply in a child class loader. |
| return null; |
| } |
| } else { |
| return null; |
| } |
| } |
| } |
| |
| /** |
| * Try to load Finalizer in its own class loader. If Finalizer's thread had a direct reference to |
| * our class loader (which could be that of a dynamically loaded web application or OSGi bundle), |
| * it would prevent our class loader from getting garbage collected. |
| */ |
| static class DecoupledLoader implements FinalizerLoader { |
| private static final String LOADING_ERROR = "Could not load Finalizer in its own class loader." |
| + "Loading Finalizer in the current class loader instead. As a result, you will not be able" |
| + "to garbage collect this class loader. To support reclaiming this class loader, either" |
| + "resolve the underlying issue, or move Google Collections to your system class path."; |
| |
| @Override |
| public Class<?> loadFinalizer() { |
| try { |
| /* |
| * We use URLClassLoader because it's the only concrete class loader implementation in the |
| * JDK. If we used our own ClassLoader subclass, Finalizer would indirectly reference this |
| * class loader: |
| * |
| * Finalizer.class -> CustomClassLoader -> CustomClassLoader.class -> This class loader |
| * |
| * System class loader will (and must) be the parent. |
| */ |
| ClassLoader finalizerLoader = newLoader(getBaseUrl()); |
| return finalizerLoader.loadClass(FINALIZER_CLASS_NAME); |
| } catch (Exception e) { |
| logger.log(Level.WARNING, LOADING_ERROR, e); |
| return null; |
| } |
| } |
| |
| /** |
| * Gets URL for base of path containing Finalizer.class. |
| */ |
| URL getBaseUrl() throws IOException { |
| // Find URL pointing to Finalizer.class file. |
| String finalizerPath = FINALIZER_CLASS_NAME.replace('.', '/') + ".class"; |
| URL finalizerUrl = getClass().getClassLoader().getResource(finalizerPath); |
| if (finalizerUrl == null) { |
| throw new FileNotFoundException(finalizerPath); |
| } |
| |
| // Find URL pointing to base of class path. |
| String urlString = finalizerUrl.toString(); |
| if (!urlString.endsWith(finalizerPath)) { |
| throw new IOException("Unsupported path style: " + urlString); |
| } |
| urlString = urlString.substring(0, urlString.length() - finalizerPath.length()); |
| return new URL(finalizerUrl, urlString); |
| } |
| |
| /** Creates a class loader with the given base URL as its classpath. */ |
| URLClassLoader newLoader(URL base) { |
| // We use the bootstrap class loader as the parent because Finalizer by design uses |
| // only standard Java classes. That also means that FinalizableReferenceQueueTest |
| // doesn't pick up the wrong version of the Finalizer class. |
| return new URLClassLoader(new URL[] {base}, null); |
| } |
| } |
| |
| /** |
| * Loads Finalizer directly using the current class loader. We won't be able to garbage collect |
| * this class loader, but at least the world doesn't end. |
| */ |
| static class DirectLoader implements FinalizerLoader { |
| @Override |
| public Class<?> loadFinalizer() { |
| try { |
| return Class.forName(FINALIZER_CLASS_NAME); |
| } catch (ClassNotFoundException e) { |
| throw new AssertionError(e); |
| } |
| } |
| } |
| |
| /** |
| * Looks up Finalizer.startFinalizer(). |
| */ |
| static Method getStartFinalizer(Class<?> finalizer) { |
| try { |
| return finalizer.getMethod( |
| "startFinalizer", |
| Class.class, |
| ReferenceQueue.class, |
| PhantomReference.class); |
| } catch (NoSuchMethodException e) { |
| throw new AssertionError(e); |
| } |
| } |
| } |