chabala
1/18/2014 - 4:10 PM

ThreadLocalImmolater lifted from somewhere online. I apply it in the Neo4J shutdown hook, to clean up Neo4J threads on hot Tomcat redeploys

ThreadLocalImmolater lifted from somewhere online. I apply it in the Neo4J shutdown hook, to clean up Neo4J threads on hot Tomcat redeploys (Running Neo4J embedded in a web app). Note that you would apply this after calling graph.shutdown(). It is intended to clean up threads that are left behind after attempting a graceful shutdown of the registry/database. Change the snippet to Neo4JService.java if you are not using Tapestry IOC.


import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.Field;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author Administrator
 * 
 *         google-gson issue # 402: Memory Leak in web application; comment # 25
 *         https://code.google.com/p/google-gson/issues/detail?id=402
 */
public class ThreadLocalImmolater {

	final Logger logger = LoggerFactory.getLogger(ThreadLocalImmolater.class);

	Boolean debug;

	public ThreadLocalImmolater() {
		debug = true;
	}

	public Integer immolate() {
		int count = 0;
		try {
			final Field threadLocalsField = Thread.class
					.getDeclaredField("threadLocals");
			threadLocalsField.setAccessible(true);
			final Field inheritableThreadLocalsField = Thread.class
					.getDeclaredField("inheritableThreadLocals");
			inheritableThreadLocalsField.setAccessible(true);
			for (final Thread thread : Thread.getAllStackTraces().keySet()) {
				count += clear(threadLocalsField.get(thread));
				count += clear(inheritableThreadLocalsField.get(thread));
			}
			logger.info("immolated " + count + " values in ThreadLocals");
		} catch (Exception e) {
			throw new Error("ThreadLocalImmolater.immolate()", e);
		}
		return count;
	}

	private int clear(final Object threadLocalMap) throws Exception {
		if (threadLocalMap == null)
			return 0;
		int count = 0;
		final Field tableField = threadLocalMap.getClass().getDeclaredField(
				"table");
		tableField.setAccessible(true);
		final Object table = tableField.get(threadLocalMap);
		for (int i = 0, length = Array.getLength(table); i < length; ++i) {
			final Object entry = Array.get(table, i);
			if (entry != null) {
				final Object threadLocal = ((WeakReference) entry).get();
				if (threadLocal != null) {
					log(i, threadLocal);
					Array.set(table, i, null);
					++count;
				}
			}
		}
		return count;
	}

	private void log(int i, final Object threadLocal) {
		if (!debug) {
			return;
		}
		if (threadLocal.getClass() != null
				&& threadLocal.getClass().getEnclosingClass() != null
				&& threadLocal.getClass().getEnclosingClass().getName() != null) {

			logger.info("threadLocalMap(" + i + "): "
					+ threadLocal.getClass().getEnclosingClass().getName());
		} else if (threadLocal.getClass() != null
				&& threadLocal.getClass().getName() != null) {
			logger.info("threadLocalMap(" + i + "): "
					+ threadLocal.getClass().getName());
		} else {
			logger.info("threadLocalMap(" + i
					+ "): cannot identify threadlocal class name");
		}
	}

}
	// This will close the graph when the registry is shutdown.
	@PostInjection
	public void listenForShutdown(RegistryShutdownHub hub) {
		hub.addRegistryShutdownListener(new Runnable() {
			public void run() {

				if (graphDb != null) {
					System.out
							.println("[[[[[[[[[[[[[[[[[[listenForShutdown: Shuting down Neo4JService because Tapestry registry is shutting down]]]]]]]]]]]]]]]]]]");
					graphDb.shutdown();
					JVMHelper.immolativeShutdown();
				} else {
					System.out
							.println("[[[[[[[[[[[[[[[[[[listenForShutdown: Neo4JService was already shut down]]]]]]]]]]]]]]]]]]");
				}
			}
		});
	}

public class JVMHelper {
	private static final Runtime runtime = Runtime.getRuntime();

	/**
	 * Aggressively suggest to free memory, then return the amount of free
	 * memory in the system.
	 * 
	 * @return
	 */
	public static long getFreeMem() {
		doGarbage();
		return runtime.freeMemory();
	}

	public static void immolativeShutdown() {
		suggestGC();
		ThreadLocalImmolater i = new ThreadLocalImmolater();
		i.immolate();
	}

	/**
	 * Aggressively suggest to free memory, then returns the total amount of
	 * memory in the Java virtual machine
	 * 
	 * @return
	 */
	public static long getTotalMem() {
		doGarbage();
		return runtime.totalMemory();
	}

	private static void doGarbage() {
		collectGarbage();
		collectGarbage();
	}

	/*
	 * SUGGEST to run the garbage collector. Remember, we can only suggest that
	 * it be called, we can't force it to be done.
	 */
	public static void suggestGC() {
		runtime.gc();
	}

	/**
	 * 
	 */
	static void collectGarbage() {
		try {
			System.gc();
			Thread.sleep(100);
			System.runFinalization();
			Thread.sleep(100);
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}

	}

}