JreMemoryLeakPreventionListener引发的报错问题

There is a memory leak detection feature introduced in Tomcat 6.0.25 that attempts to log objects that have failed to be unregistered by webapps it hosts when they are stopped, and were forcibly unregistered by Tomcat. As Tomcat is forcibly removing these objects, it is not a serious concern that these log messages occur.

停止tomcat服务后,报出以下错误:

严重: The web application [] appears to have started a thread named [Timer-0] but has failed to stop it. This is very likely to create a memory leak.
2016-9-27 21:33:48 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
严重: The web application [] appears to have started a thread named [pool-1-thread-1] but has failed to stop it. This is very likely to create a memory leak.
2016-9-27 21:33:48 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
严重: The web application [] appears to have started a thread named [DefaultQuartzScheduler_Worker-2] but has failed to stop it. This is very likely to create a memory leak.
2016-9-27 21:33:48 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads

初步诊断为服务停止时,线程池未能成功关闭,添加线程监听器ThreadListener,在服务停止的时候停止线程池:

public class ThreadListener implements ServletContextListener {

	private Log logger = LogFactory.getLog(ThreadListener.class);
	
	@Override
	public void contextInitialized(ServletContextEvent sce) {
		
	}

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		try {
			logger.info("销毁线程池");
			ProtoExecutorService.getExecutor().shutdownNow();
		} catch (Exception e) {
			logger.error(String.format("销毁线程池出错,错误信息:%s", e));
			e.printStackTrace();
		}
	}
}

继续运行一段时间后发现,停止tomcat服务后仍会出现同样报错,查找资料,http://stackoverflow.com/questions/5292349/is-this-very-likely-to-create-a-memory-leak-in-tomcat ,添加清理ThreadLocal的代码:

public class ThreadLocalImmolater {

	private Log logger = LogFactory.getLog(ThreadLocalImmolater.class);

	private static ThreadLocalImmolater immolater = null;
	
    Boolean debug;

    public ThreadLocalImmolater() {
        debug = false;
    }
    
    public synchronized static ThreadLocalImmolater getInstance(){
    	if(null == immolater){
    		immolater = new ThreadLocalImmolater();
    	}
    	return immolater;
    }
    
    public Integer immolate() throws Exception {
        int count = 0;
        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));
            
            if (thread != null) {
            	thread.setContextClassLoader(null);
            }
        }
        logger.info("immolated " + count + " values in ThreadLocals");
        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");
        }
    }

}

在ThreadListener中添加以下代码:

//清理线程
try {
	logger.info("清理线程");
	ThreadLocalImmolater.getInstance().immolate();
} catch (Exception e) {
	logger.error(String.format("清理线程出错,错误信息:%s", e));
	e.printStackTrace();
}

运行一段时间后发现,停止tomcat服务后不再出现同样错误,但是会出现

严重: The web application [] registered the JDBC driver [com.microsoft.sqlserver.jdbc.SQLServerDriver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.

在ThreadListener中添加以下代码:

logger.info("关闭数据库连接");
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
	Driver driver = drivers.nextElement();
	try {
		DriverManager.deregisterDriver(driver);
		logger.info(String.format("deregistering jdbc driver: %s", driver));
	} catch (SQLException e) {
		logger.error(String.format("Error deregistering driver %s", driver), e);
	}

}

报错消失。

参考资料:

http://fourfireliu.iteye.com/blog/2187584

http://stackoverflow.com/questions/5292349/is-this-very-likely-to-create-a-memory-leak-in-tomcat

转载于:https://my.oschina.net/u/921069/blog/755768

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值