Created
January 18, 2014 16:10
-
-
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…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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]]]]]]]]]]]]]]]]]]"); | |
} | |
} | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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