Over a million developers have joined DZone.

FinalizableReferenceQueue

·
FinalizableReferenceQueue which spawns a thread that doesn't strongly reference the library's class loader. If the background thread has a strong reference to the library's class loader, the library is loaded as part of a web app, and you try to dynamically reload that web app, the old instance of the web app will leak.


/*
 * Copyright (C) 2007 Google Inc.
 *
 * 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.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.PhantomReference;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * Starts a background thread that cleans up after reclaimed referents.
 *
 * 

Most of this code goes to preventing creation of a strong reference * from the thread to this class's loader so we can support reloading in * application servers. * * @author Bob Lee */ class FinalizableReferenceQueue extends ReferenceQueue { private static final Logger logger = Logger.getLogger(FinalizableReferenceQueue.class.getName()); private static final ReferenceQueue queue = startFinalizer(); /** * Returns the singleton instance. */ public static ReferenceQueue getInstance() { return queue; } private static final String FINALIZER_CLASS_NAME = "com.google.common.base.Finalizer"; /** * Starts the finalizer thread. */ private static ReferenceQueue startFinalizer() { try { ClassLoader systemLoader = ClassLoader.getSystemClassLoader(); if (systemLoader != null) { /* * If the class is already in the system class path, don't bother * creating a new class loader. */ try { if (systemLoader.loadClass(FINALIZER_CLASS_NAME) != null) { //return startFinalizerDirectly(); } } catch (ClassNotFoundException e) { /* ignore */ } } // Read Finalizer class bytes from class loader. ClassLoader thisLoader = FinalizableReferenceQueue.class.getClassLoader(); String fileName = "/" + FINALIZER_CLASS_NAME.replace('.', '/') + ".class"; InputStream in = thisLoader.getResourceAsStream(fileName); if (in == null) { throw new IOException("Could not find " + fileName + " in class path."); } // Copy bytes into a byte[]. ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int read; while ((read = in.read(buffer)) > -1) { out.write(buffer, 0, read); } in.close(); byte[] finalizerBytes = out.toByteArray(); /* * We use URLClassLoader because it's the only concrete class loader * implementation in the JDK. If we used our own ClassLoader subclass, * Finalizer would have a reference to our custom class loader instance, * which would reference its class, which would reference the application * class loader, which we're trying to prevent. */ ClassLoader loader = new URLClassLoader(new URL[0], systemLoader); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); defineClass.setAccessible(true); Class finalizer = (Class) defineClass.invoke(loader, FINALIZER_CLASS_NAME, finalizerBytes, 0, finalizerBytes.length); // Give Finalizer a reference to this class so it knows when to stop. Method backReference = finalizer.getDeclaredMethod( "backReference", Class.class); backReference.setAccessible(true); backReference.invoke(null, FinalizableReferenceQueue.class); return extractQueue(finalizer); } catch (Exception e) { logger.log(Level.WARNING, "Could not load " + FINALIZER_CLASS_NAME + " in its own class loader.", e); // Fall back to loading Finalizer directly. return startFinalizerDirectly(); } } /** * Loads the finalizer in this class loader and starts it. */ private static ReferenceQueue startFinalizerDirectly() { try { return extractQueue(Class.forName(FINALIZER_CLASS_NAME)); } catch (ClassNotFoundException e) { throw new AssertionError(e); } } /** * Copies a queue reference from Finalizer into this class. */ @SuppressWarnings("unchecked") private static ReferenceQueue extractQueue(Class finalizer) { try { Field queueField = finalizer.getDeclaredField("queue"); queueField.setAccessible(true); return (ReferenceQueue) queueField.get(null); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } } /** * Thread that finalizes referents. All references should implement Runnable. * * THE APPLICATION CLASS LOADER SHOULD *NOT* LOAD THIS CLASS DIRECTLY. THIS * CLASS AND ITS CLASS LOADER SHOULD NOT STRONGLY REFERENCE THE APPLICATION * CLASS LOADER. * * Doing so would create a strong reference from a thread to an application * class which would keep the garbage collector from reclaiming the * application class loader, thereby breaking reloading of applications * in application servers by leaking memory. */ class Finalizer extends Thread { private static final Logger logger = Logger.getLogger(Finalizer.class.getName()); /** * Reference queue. The application will grab a reference to this * reflectively. */ static final ReferenceQueue queue = new ReferenceQueue(); private static PhantomReference> backReference; /** * Creates a reference back to the application class loader so we can find * out if it has been garbage collected. Once it has, we can safely stop * the finalizer thread. */ static void backReference(Class clazz) { backReference = new PhantomReference>(clazz, queue); } static { // Start background thread. new Finalizer().start(); } /** Constructs a new finalizer thread. */ private Finalizer() { super(Finalizer.class.getSimpleName()); setDaemon(true); } /** * Loops continuously, pulling references off the queue and cleaning them up. */ @Override public void run() { while (true) { try { Reference reference = queue.remove(); if (reference == backReference) { // The application class loader has been garbage collected. We can // stop and be collected ourselves. // Theoretically, this is the last reference in the queue. return; } cleanUp(reference); } catch (InterruptedException e) { /* ignore */ } } } /** * Cleans up a single reference. Catches and logs all throwables. */ private void cleanUp(Reference reference) { try { // TODO: We really don't want to make the FinalizableXxxReference classes // implement Runnable (it would expose the finalization in the public // API!). We need to pass FinalizableReference.class into this class // (perhaps in backReference()) and then reflectively invoke // finalizeReferent(). ((Runnable) reference).run(); } catch (Throwable t) { logger.log(Level.SEVERE, "Error cleaning up after reference.", t); } } }
Topics:

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}