Salmon的全栈知识 Salmon的全栈知识
首页
  • JavaSE
  • JavaWeb
  • Spring生态
  • JUC
  • JVM
  • Netty
  • Java各版本特性
  • 23种设计模式
  • Maven
  • Java常用框架
  • Dubbo
  • OpenFeign
  • Nacos
  • Zookeeper
  • Sentinel
  • Seata
  • Gateway
  • Go基础
  • Gin
  • SQL数据库

    • MySQL
    • Oracle
  • NoSQL数据库

    • Redis
    • MongoDB
    • ElasticSearch
  • 消息中间件

    • RabbitMQ
    • RocketMQ
    • Kafka
    • ActiveMQ
    • MQTT
    • NATS
  • 网关中间件

    • Nginx
  • Linux
  • Docker
  • Git
  • K8s
  • Solidity
  • Java
  • 计算机网络
  • 操作系统
GitHub (opens new window)
首页
  • JavaSE
  • JavaWeb
  • Spring生态
  • JUC
  • JVM
  • Netty
  • Java各版本特性
  • 23种设计模式
  • Maven
  • Java常用框架
  • Dubbo
  • OpenFeign
  • Nacos
  • Zookeeper
  • Sentinel
  • Seata
  • Gateway
  • Go基础
  • Gin
  • SQL数据库

    • MySQL
    • Oracle
  • NoSQL数据库

    • Redis
    • MongoDB
    • ElasticSearch
  • 消息中间件

    • RabbitMQ
    • RocketMQ
    • Kafka
    • ActiveMQ
    • MQTT
    • NATS
  • 网关中间件

    • Nginx
  • Linux
  • Docker
  • Git
  • K8s
  • Solidity
  • Java
  • 计算机网络
  • 操作系统
GitHub (opens new window)
npm

(进入注册为作者充电)

  • JVM基础

    • 初识JVM
    • 字节码文件详解
    • 运行时数据区
    • 垃圾回收
  • JVM实战

    • 内存调优
    • GC调优
    • 性能调优
  • JVM高级

    • GraalVM
    • 新一代的GC
    • 揭秘Java工具
  • JVM原理

    • 栈上的数据存储
    • 对象在堆上是如何存储的?
    • 方法调用的原理
    • 异常捕获的原理
    • JIT即时编译器
    • 垃圾回收器原理
  • JVM面试

    • 什么是JVM?
    • 了解过字节码文件的组成吗?
    • 说一下运行时数据区
    • 哪些区域会出现内存溢出,会有什么现象?
    • JVM在JDK6-8之间在内存区域上有什么不同
    • 类的生命周期
    • 什么是类加载器?
    • 什么是双亲委派机制
    • 如何打破双亲委派机制
    • Tomcat的自定义类加载器
      • 环境搭建:
      • common类加载器
      • catalina类加载器
      • shared类加载器
      • ParallelWebappClassLoader类加载器
      • JasperLoader类加载器
      • 总结:
    • 如何判断堆上的对象没有被引用??
    • JVM 中都有哪些引用类型?
    • ThreadLocal中为什么要使用弱引用?
    • 有哪些常见的垃圾回收算法?
    • 有哪些常用的垃圾回收器?
    • 如何解决内存泄漏问题?
    • 常见的JVM参数?
  • 《JVM》笔记
  • JVM面试
Salmon
2024-03-12
目录
环境搭建:
common类加载器
catalina类加载器
shared类加载器
ParallelWebappClassLoader类加载器
JasperLoader类加载器
总结:

Tomcat的自定义类加载器

Tomcat中,实现了一套自定义的类加载器。这一小节使用目前应用比较广泛的Tomcat9(9.0.84)源码进行分析。

# 环境搭建:

可以直接运行代码文件夹下的tomcat源码,这个项目已经修改过源码了,但是为了保证将来版本更新后同学们还能搭建最新代码版本的环境,将搭建过程在这里记录一下。

1、打开tomcat找到9.0版本的源代码,下载:

img

2、使用Idea打开,整个项目没有使用Maven,为了方便进行项目管理。在项目中创建pom文件,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>
  <groupId>org.apache.tomcat</groupId>
  <artifactId>tomcat</artifactId>
  <name>tomcat</name>
  <version>c</version>

  <build>
    <finalName>tomcat</finalName>
    <sourceDirectory>java</sourceDirectory>
    <!--<testSourceDirectory>test</testSourceDirectory>-->
    <resources>
      <resource>
        <directory>java</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>test</directory>
      </testResource>
    </testResources>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.6.1</version>
        <configuration>
          <encoding>UTF-8</encoding>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.easymock</groupId>
      <artifactId>easymock</artifactId>
      <version>4.0.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.ant</groupId>
      <artifactId>ant</artifactId>
      <version>1.10.8</version>
    </dependency>
    <dependency>
      <groupId>wsdl4j</groupId>
      <artifactId>wsdl4j</artifactId>
      <version>1.6.2</version>
    </dependency>
    <dependency>
      <groupId>javax.xml</groupId>
      <artifactId>jaxrpc</artifactId>
      <version>1.1</version>
    </dependency>

    <dependency>
      <groupId>org.eclipse.jdt.core.compiler</groupId>
      <artifactId>ecj</artifactId>
      <version>4.6.1</version>
    </dependency>
    <dependency>
      <groupId>biz.aQute.bnd</groupId>
      <artifactId>biz.aQute.bndlib</artifactId>
      <version>5.1.1</version>
    </dependency>

    <dependency>
      <groupId>com.unboundid</groupId>
      <artifactId>unboundid-ldapsdk</artifactId>
      <version>4.0.13</version>
      <scope>test</scope>
    </dependency>

  </dependencies>
</project>

3、选择Application,添加应用。JDK选择JDK8:

img

4、添加JVM参数:

 -Duser.language=en   -Duser.region=US

5、将JDTCompiler.java中这一段内容报错删除,这里我已经删除了:

img

6、在StringManager.java中添加这段代码,强制用iso8859-1编码获取字符串。

img

try {
    value = new String(value.getBytes("iso8859-1"),"utf-8");
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}

7、在ContextConfig.java文件中添加JSP初始化器:

img

context.addServletContainerInitializer(new JasperInitializer(),null);

8、将web项目放入webapps目录下,运行项目:

img

Tomcat类加载器整体结构图如下:

img

# common类加载器

common类加载器主要加载tomcat自身使用以及应用使用的jar包,默认配置在catalina.properties文件中。

common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar"

img

debug调试common累类加载器初始化代码:

img

它是URLClassLoader类的子类对象,根据URL获取jar包中的class字节码文件。

# catalina类加载器

catalina类加载器主要加载tomcat自身使用的jar包,不让应用使用,默认配置在catalina.properties文件中。

server.loader= 默认配置为空,为空时catalina加载器和common加载器是同一个。

配置参数之前,catalina加载器其实就是common加载器:

img

配置catalina加载的路径:

img

这次就创建了一个新的Catalina类加载器,专门加载配置目录下的类:

img

# shared类加载器

shared类加载器主要加载应用使用的jar包,不让tomcat使用,默认配置在catalina.properties文件中。

shared.loader= 默认配置为空,为空时shared加载器和common加载器是同一个。

img

# ParallelWebappClassLoader类加载器

ParallelWebappClassLoader类加载器可以多线程并行加载应用中使用到的类,每个应用都拥有一个自己的该类加载器。

demo1项目的类加载器:

img

demo2的类加载器:

img

为什么每个应用会拥有一个独立的ParallelWebappClassLoader类加载器呢?

同一个类加载器,只能加载一个同名的类。两个应用中相同名称的类都必须要加载。

img

所以tomcat的做法是为每个应用创建一个web应用类加载器:

img

ParallelWebappClassLoader的执行流程:

img

默认这里打破了双亲委派机制,应用中的类如果没有加载过。会先从当前类加载器加载,然后再交给父类加载器通过双亲委派机制加载。

img

# JasperLoader类加载器

JasperLoader类加载器负责加载JSP文件编译出来的class字节码文件,为了实现热部署(不重启让修改的jsp生效),每一个jsp文件都由一个独立的JasperLoader负责加载。

访问一个JSP文件,会触发JasperLoader类加载器的创建:

img

修改JSP文件:

img

用Arthas看类加载器情况:

img

类加载器数量变多了。

# 总结:

img

上次更新: 2025/03/09, 18:29:07
如何打破双亲委派机制
如何判断堆上的对象没有被引用??

← 如何打破双亲委派机制 如何判断堆上的对象没有被引用??→

Theme by Vdoing | Copyright © 2022-2025 Salmon's Blog
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式