Tomcat-ClassLoader-机制与热部署原理
目录
Tomcat ClassLoader 机制与热部署原理
Tomcat ClassLoader 机制与热部署原理
1. 引言
在前面的几篇文章中,我们已经分析了 Tomcat 的 启动流程、容器体系、Servlet 执行链路、线程模型和 Session 管理。
Web 应用的 隔离和热部署 依赖于 Tomcat 自定义的 ClassLoader 体系。
本篇文章将详细剖析:
- Tomcat 的 ClassLoader 层次与工作机制
- Web 应用的类隔离与双亲委派破坏
- 热部署(Hot Deployment)原理与内存泄漏防护
- 结合源码解析关键类
2. Tomcat ClassLoader 层次结构
Tomcat 的类加载器体系如下:
BootstrapClassLoader (JVM内置)
↓
SystemClassLoader (AppClassLoader)
↓
CommonClassLoader (加载 $CATALINA_HOME/lib)
├── CatalinaClassLoader (Tomcat 核心类)
├── SharedClassLoader (共享库)
└── WebAppClassLoader (每个 Web 应用独立)
- BootstrapClassLoader:加载 JDK 核心类
- SystemClassLoader:加载系统类路径下的类
- CommonClassLoader:加载 Tomcat 核心库和共享库
- WebAppClassLoader:每个 Web 应用独立,加载
WEB-INF/classes
和WEB-INF/lib
3. 破坏双亲委派机制
双亲委派机制
默认 Java 类加载器遵循 双亲委派:
- 先委托父类加载器加载类
- 父类加载器找不到才由当前类加载器加载
Tomcat 的改造
WebAppClassLoaderBase 覆盖了 loadClass
方法:
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(name);
if (clazz == null) {
try {
// 先尝试自己加载(破坏双亲委派)
clazz = findClass(name);
} catch (ClassNotFoundException e) {
// 找不到再交给父加载器
clazz = super.loadClass(name, resolve);
}
}
return clazz;
}
- 保证 Web 应用自己的类优先加载
- 支持不同应用使用不同版本的库
- 避免冲突和覆盖
4. 热部署原理
4.1 热部署流程
当新的 WAR 文件部署或替换时:
停止旧 Context
- 调用
Context.stop()
,销毁 Servlet - 停止线程、关闭资源
- 调用 Listener 的
contextDestroyed
- 调用
卸载旧 ClassLoader
- WebAppClassLoaderBase 被 GC 回收
- 清理线程、Timer、JDBC 连接等潜在引用
创建新 Context
- 使用新的 WebAppClassLoader 加载新应用
- 初始化 Servlet、Filter、Listener
启用新应用
- 请求路由到新应用
4.2 流程图
部署新 WAR
↓
Context.stop() → Wrapper.destroy()
↓
WebAppClassLoader 卸载 → GC 回收
↓
创建新 ClassLoader → Context.init()
↓
Servlet/Filter/Listener 初始化
5. 核心源码解析
(1) WebAppClassLoaderBase
public class WebAppClassLoaderBase extends ClassLoader {
protected Class<?> loadClass(String name, boolean resolve) {
// 自己优先加载
Class<?> clazz = findLoadedClass(name);
if (clazz == null) {
try {
clazz = findClass(name);
} catch (ClassNotFoundException e) {
clazz = super.loadClass(name, resolve);
}
}
return clazz;
}
public void stop() {
// 清理 Loader 缓存、关闭 Jar 文件
clearReferences();
}
}
(2) Context.stop()
public class StandardContext extends ContainerBase implements Context {
@Override
protected void stopInternal() throws LifecycleException {
// 停止所有 Servlet
unloadServlets();
// 停止 WebAppClassLoader
loader.stop();
}
}
(3) 内存泄漏防护
Tomcat 在热部署时特别注意内存泄漏:
- 清理 ThreadLocal
- 关闭定时器(Timer/Executor)
- 清理 JDBC 连接池
- 卸载 WebAppClassLoader
否则旧类和对象无法被 GC 回收,导致 PermGen/Metaspace 泄漏。
6. 热部署配置示例
在 server.xml
中启用自动部署:
<Host name="localhost" appBase="webapps" autoDeploy="true" deployOnStartup="true">
<Context path="/demo" docBase="demo" reloadable="true"/>
</Host>
- autoDeploy=true:自动检测 WAR 文件
- deployOnStartup=true:Tomcat 启动时部署应用
- reloadable=true:类变化时自动重新加载
7. 总结
本篇文章系统解析了 Tomcat ClassLoader 机制与热部署原理:
- ClassLoader 层次:Bootstrap → System → Common → WebAppClassLoader
- 破坏双亲委派:Web 应用类优先加载
- 热部署流程:Context.stop() → ClassLoader 卸载 → Context.init()
- 内存泄漏防护:清理线程、Timer、ThreadLocal、JDBC
- 配置:reloadable、autoDeploy、deployOnStartup
通过理解 ClassLoader 与热部署机制,可以安全地在生产环境实现 Web 应用的无缝升级和动态加载。