Skip to content

Instantly share code, notes, and snippets.

@jnehlmeier
Created November 18, 2013 20:02
Show Gist options
  • Save jnehlmeier/7534390 to your computer and use it in GitHub Desktop.
Save jnehlmeier/7534390 to your computer and use it in GitHub Desktop.
Utility class used in ServletContextListener to fix various memory leaks in application servers
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import java.awt.*;
import java.lang.reflect.Field;
import java.util.logging.LogManager;
public class LeakProtection {
private static final Logger logger = LoggerFactory.getLogger(LeakProtection.class);
private static Field inheritableThreadLocalsField;
static {
try {
inheritableThreadLocalsField = Thread.class.getDeclaredField("inheritableThreadLocals");
inheritableThreadLocalsField.setAccessible(true);
} catch (NoSuchFieldException e) {
logger.warn("'inheritableThreadLocals' not found in Thread.class. " +
"LeakProtection.newThread() und LeakProtection.clearInheritableThreadLocals() won't work.", e);
}
}
public static void fixJavaUtilLogLeaks() {
LogManager lm = LogManager.getLogManager();
logger.info("Memory Leak fixed: LogManager ({})", lm);
}
/**
* Fixes various AWT leaks that might occur in application servers
*/
public static void fixAwtLeaks() {
// code executed in a custom thread to make sure no inheritableThreadLocals will leak into
// threads that might be created by AWT and are out of our control (e.g. 2D Disposer thread)
Runnable runnable = new Runnable() {
@Override
public void run() {
//Replace WebAppClassLoader with system ClassLoader while executing this Runnable
Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
try {
sun.awt.AppContext context = sun.awt.AppContext.getAppContext();
logger.info("Memory Leak fixed: sun.awt.AppContext initialized. ({})", context);
Class.forName("sun.java2d.Disposer").newInstance();
logger.info("Memory Leak fixed: Java2D Disposer Thread initialized.");
ImageIO.setUseCache(false);
ImageIO.getCacheDirectory();
logger.info("Memory Leak fixed: ImageIO System initialized.");
Font arial = Font.decode( "Arial-BOLD-18" );
String font = arial.toString();
logger.info("Memory Leak fixed: Font System initialized. ({})", font);
GraphicsEnvironment g = GraphicsEnvironment.getLocalGraphicsEnvironment();
logger.info("Memory Leak fixed: GraphicsEnvironment initialized. ({})", g);
Toolkit t = Toolkit.getDefaultToolkit();
if(t != null) {
t.getSystemEventQueue();
}
logger.info("Memory Leak fixed: DefaultToolkit initialized. ({})", t);
} catch (Throwable e) {
logger.warn("Unexpected Exception occurred during memory leak fixing. Memory leaks might be possible.", e);
}
}
};
Thread t = newThread("LeakProtection - Fix AWT leaks", runnable);
t.start();
try {
t.join();
} catch (InterruptedException e) {
logger.warn("Thread '{}' interrupted!", t.getName(), e);
}
}
/**
* Sets the 'inheritableThreadLocals' field of Thread.class to null for a given target.
*/
public static void clearInheritableThreadLocals(Thread target) {
if(inheritableThreadLocalsField == null) {
return;
}
try {
inheritableThreadLocalsField.set(target, null);
} catch (Throwable e) {
logger.warn("Can not set Thread.inheritableThreadLocals to NULL. {}", target.getName(), e);
}
}
/**
* Creates a new thread that does not have any inheritableThreadLocals.
*
* @param threadName name of the thread
* @param target runnable to execute
* @return new thread without any inheritableThreadLocals
*/
public static Thread newThread(String threadName, Runnable target) {
Thread t = new Thread(target, threadName);
clearInheritableThreadLocals(t);
return t;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment