DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world
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.
*
* <p>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<Object> {
private static final Logger logger
= Logger.getLogger(FinalizableReferenceQueue.class.getName());
private static final ReferenceQueue<Object> queue = startFinalizer();
/**
* Returns the singleton instance.
*/
public static ReferenceQueue<Object> getInstance() {
return queue;
}
private static final String FINALIZER_CLASS_NAME
= "com.google.common.base.Finalizer";
/**
* Starts the finalizer thread.
*/
private static ReferenceQueue<Object> 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<Object> 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<Object> extractQueue(Class<?> finalizer) {
try {
Field queueField = finalizer.getDeclaredField("queue");
queueField.setAccessible(true);
return (ReferenceQueue<Object>) 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<Object> queue = new ReferenceQueue<Object>();
private static PhantomReference<Class<?>> 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<Class<?>>(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);
}
}
}





