Skip to content

Instantly share code, notes, and snippets.

@danieljue
Created January 18, 2014 16:10
Show Gist options
  • Save danieljue/8492479 to your computer and use it in GitHub Desktop.
Save danieljue/8492479 to your computer and use it in GitHub Desktop.
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 regis…
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();
}
}
}
// 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]]]]]]]]]]]]]]]]]]");
}
}
});
}
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");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment