tomcat reload时内存泄漏的处理 hot deployment_第1页
tomcat reload时内存泄漏的处理 hot deployment_第2页
tomcat reload时内存泄漏的处理 hot deployment_第3页
tomcat reload时内存泄漏的处理 hot deployment_第4页
tomcat reload时内存泄漏的处理 hot deployment_第5页
已阅读5页,还剩1页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、我做的应用是以Spring为系统的基础框架,mysql为后台数据库.在tomcat上发布后,总是不能进行热部署(reload),多次reload后,就会出OutOfMemory PermGen,为此烦恼了很久,总于下定决心找找根源.经过3天的不懈努力,小有成果,记录下来实际上下面的分析都已经没什么用了,如果你使用tomcat6.0.26及以后的版本,我所说的这些情况都已经被处理了,并且比我处理的还要多很多.可以下载tomcat6.0.26的源代码看看WebappClassLoader类的处理就成了.通过分析工具的分析(用了YourKit,以及JDK1.6/bin下的jps/jmap/jhat)

2、,发现有下面几个方面会造成memory leak.1.SystemClassLoader与WebappClassLoader加载的类相互引用,tomcat reload只是卸载WebappClassloader中的class,SystemClassLoader是不会卸载的(否则其他应用也停止了).但是WebappClassloader加载的类被SystemClassLoader引用的化,WebappClassloader中的相关类就不会被JVM进行垃圾收集ThreadLocal.set(Object),如果这个Object是WebappsClassLoader加载的,使用之后没有做Thread

3、Local.set(null)或者ThreadLocal.remove(),就会产生memory leak.由于ThreadLocal实际上操作的是java.lang.Thread类中的ThreadLocalMap,Thread类是由SystemClassLoder加载的.而这个线程实例(main thread)在tomcat reload的时候不会销毁重建,必然就产生了SystemClassLoder中的类引用WebappsClassLoader的类.DriverManager也是由SystemClassLoder载入的,当初始化某个JDBC驱动的时候,会向DriverManager中注册该

4、驱动,通常是*.driver,例如com.mysql.jdbc.Driver这个Driver是通过class.forName()加载的,通常也是加载到WebappClassLoader.这就出现了两个classLoader中的类的交叉引用.导致memory leak.解决办法:写一个ServletContextListener,在contextDestroyed方法中统一删除当前Thread的ThreadLocalMap中的内容.public class ApplicationCleanListener implements ServletContextListener public void

5、 contextInitialized(ServletContextEvent event) public void contextDestroyed(ServletContextEvent event)          /处理ThreadLocal   ThreadLocalCleanUtil.clearThreadLocals();   /*   * 如果数据故驱动是通过应用服务器(tomcat etc.)中配置的<公用>连接池,这里不需

6、要 否则必须卸载Driver   *    * 原因: DriverManager是System classloader加载的, Driver是webappclassloader加载的,   * driver保存在DriverManager中,在reload过程中,由于system   * classloader不会销毁,driverManager就一直保持着对driver的引用,   * driver无法卸载,与driver关联的其他类   * ,例如DataSo

7、urce,jdbcTemplate,dao,service.都无法卸载   */   try     System.out.println("clean jdbc Driver.");    for (Enumeration e = DriverManager.getDrivers(); e      .hasMoreElements();)      Driver driver = (

8、Driver) e.nextElement();     if (driver.getClass().getClassLoader() = getClass()       .getClassLoader()       DriverManager.deregisterDriver(driver);            catch (Exception e

9、)     System.out      .println("Exception cleaning up java.sql.DriverManager's driver: "        + e.getMessage();   /* 这个类根据*/public class ThreadLocalCleanUtil /* 得到当前线程组中的所有线程 description:* * ret

10、urn*/private static Thread getThreads()    ThreadGroup tg = Thread.currentThread().getThreadGroup();   while (tg.getParent() != null)     tg = tg.getParent();      int threadCountGuess = tg.activeCount() + 50;   Thread threads = new Th

11、readthreadCountGuess;   int threadCountActual = tg.enumerate(threads);   while (threadCountActual = threadCountGuess)     threadCountGuess *= 2;    threads = new ThreadthreadCountGuess;    threadCountActual = tg.enumerate(threads);

12、0;     return threads;public static void clearThreadLocals()    ClassLoader classloader = Thread     .currentThread()     .getContextClassLoader();   Thread threads = getThreads();   try     Field thr

13、eadLocalsField = Thread.class      .getDeclaredField("threadLocals");    threadLocalsField.setAccessible(true);    Field inheritableThreadLocalsField = Thread.class      .getDeclaredField("inheritableThrea

14、dLocals");    inheritableThreadLocalsField.setAccessible(true);    Class tlmClass = Class      .forName("java.lang.ThreadLocal$ThreadLocalMap");    Field tableField = tlmClass.getDeclaredField("table");

15、60;   tableField.setAccessible(true);    for (int i = 0; i < threads.length; +i)      if (threadsi = null)      continue;     Object threadLocalMap = threadLocalsField.get(threadsi);    

16、clearThreadLocalMap(threadLocalMap, tableField, classloader);     threadLocalMap = inheritableThreadLocalsField.get(threadsi);     clearThreadLocalMap(threadLocalMap, tableField, classloader);       catch (Exception e)   

17、0; e.printStackTrace();   private static void clearThreadLocalMap(Object map,    Field internalTableField, ClassLoader classloader)    throws NoSuchMethodException, IllegalAccessException,    NoSuchFieldException, InvocationTargetException  

18、;  if (map != null)     Method mapRemove = map.getClass().getDeclaredMethod("remove",      new Class ThreadLocal.class );    mapRemove.setAccessible(true);    Object table = (Object) internalTableField.get(map);

19、0;   int staleEntriesCount = 0;    if (table != null)      for (int j = 0; j < table.length; +j)       if (tablej != null)        boolean remove = false;       Object

20、key = (Reference) tablej).get();       if (key != null)         && (key.getClass().getClassLoader() = classloader)         remove = true;        System.

21、out.println("clean threadLocal key,class="          + key.getClass().getCanonicalName()          + ",value=" + key.toString();           

22、60;  Field valueField = tablej         .getClass()         .getDeclaredField("value");       valueField.setAccessible(true);       Object value =

23、 valueField.get(tablej);       if (value != null)         && (value.getClass().getClassLoader() = classloader)         remove = true;        System.out.

24、println("clean threadLocal value,class="          + value.getClass().getCanonicalName()          + ",value=" + value.toString();           &

25、#160;  if (remove)         if (key = null)         +staleEntriesCount;        else          mapRemove.invoke(map, new Object key );  &#

26、160;                               if (staleEntriesCount > 0)      Method mapRemoveStale = map       .getClass() 

27、0;     .getDeclaredMethod("expungeStaleEntries", new Class0);     mapRemoveStale.setAccessible(true);     mapRemoveStale.invoke(map, new Object0);       2.对于使用mysql JDBC驱动的:mysql JDBC驱动会启动一个Timer Thread,这个

28、线程在reload的时候也是无法自动销毁.因此,需要强制结束这个timer可以在 上面的ApplicationCleanListener中加入如下代码:   try     Class ConnectionImplClass = Thread      .currentThread()      .getContextClassLoader()      .loadClass("com.

29、mysql.jdbc.ConnectionImpl");    if (ConnectionImplClass != null      && ConnectionImplClass.getClassLoader() = getClass()        .getClassLoader()      System.out.println("clean mysql timer.");     Field f = ConnectionImplClass.getDeclaredField("cancelTimer");   

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论