跳到主要内容

十、Tomcat源码分析-关于Tomcat热加载的一些思考

在前面的文章中,我们分析了 tomcat 类加载器的相关源码,也了解了 tomcat 支持类的热加载,意味着 tomcat 要涉及类的重复卸装/装载过程,这个过程是很敏感的,一旦处理不当,可能会引起内存泄露

卸载类

我们知道,class 信息存放在元数据区(1.7是 Perm 区),这一块的内存相比堆而言,只占据非常小的空间,但是如果处理不当,还是有可能会导致内存溢出。这让我回想起几年前的一个故障,线上环境启用了 tomcat 的自动 reload 功能,出现过 java.lang.OutOfMemoryError: PermGen space 问题,排查的结果是因为 tomcat 在自动重载应用的时候,没有正常卸载类,导致 Perm 区内存没能被释放而发生溢出。tomcat 会尽量避免这类问题的发生,但是不能百分之百保证不会出现,所以还是建议不要随意开启 reloadable 功能

卸载类的条件很苛刻,必须同时满足以下3点:
1、 该类所有的实例已经被回收;
2、 加载该类的ClassLoder已经被回收;
3、 该类对应的java.lang.Class对象没有任何地方被引用;

针对第1点,保证所有的实例被回收,这点不难,tomcat 在 Context 组件中实例化这些对象,持有直接或间接的引用,所以在热部署的时候,只要回收 Context 组件即可保证实例对象被回收。

在前面的文章中我们分析了 tomcat 类加载器,tomcat 使用 ParallelWebappClassLoader 加载 Class,在热部署的时候自然也会回收该类加载器。但是要注意的是,ParallelWebappClassLoader 会作为线程上下文的类加载器,因此要避免该类加载器对象在其他地方被引用。其实,这个问题是最隐晦的,jdk 中有些类会持有线程上下文的类加载器,作为一个优秀的开源产品,tomcat 为我们解决了很多诸如此类的问题

此外,还要保证类对应的 java.lang.Class 对象没有任何地方引用,只要 Class 对象作用域限制在 Context 组件的作用范围便不会发生泄露,tomcat 也是这么做了,使用 Context 实现了隔离机制

热加载问题

热加载会面临很多问题,有很多坑,需要非常丰富的经验。下面针对 tomcat 中涉及的类加载器泄露、对象泄露、文件锁等这几类常见的问题加以分析讨论。如果您对热加载感兴趣的话,可以研究下阿里开源的 jarlinks