目录

Tomcat-ClassLoader-机制与热部署原理

Tomcat ClassLoader 机制与热部署原理

Tomcat ClassLoader 机制与热部署原理

https://i-blog.csdnimg.cn/direct/c9d6cdfee4504b8f92dbc8a73b76b167.png

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/classesWEB-INF/lib

3. 破坏双亲委派机制

双亲委派机制

默认 Java 类加载器遵循 双亲委派

  1. 先委托父类加载器加载类
  2. 父类加载器找不到才由当前类加载器加载

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 文件部署或替换时:

  1. 停止旧 Context

    • 调用 Context.stop(),销毁 Servlet
    • 停止线程、关闭资源
    • 调用 Listener 的 contextDestroyed
  2. 卸载旧 ClassLoader

    • WebAppClassLoaderBase 被 GC 回收
    • 清理线程、Timer、JDBC 连接等潜在引用
  3. 创建新 Context

    • 使用新的 WebAppClassLoader 加载新应用
    • 初始化 Servlet、Filter、Listener
  4. 启用新应用

    • 请求路由到新应用

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 应用的无缝升级和动态加载