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
文件锁
在 Windows 系统下使用 URLConnection
读取本地 jar 包的资源时,它会将资源缓存起来,会导致该 jar 包资源被锁。如果这个时候使用 war 包进行重新部署,需要解压 war 包再把原来目录下面的 jar 包删除,由于 jar 包资源被锁,导致删除失败,重新部署自然也会失败。我们先来看一段代码,这段代码会抛出异常,java.nio.file.FileSystemException: E:\spring-boot-2.0.1.RELEASE.jar: 另一个程序正在使用此文件,进程无法访问
,说明该 jar 包被锁了
String path = "E://spring-boot-2.0.1.RELEASE.jar";
File file = new File( path );
URL url = file.toURI().toURL();URLConnection uConn = url.openConnection();
uConn.getLastModified(); // 读取jar包信息
为了解决文件锁的问题,tomcat 禁用了 URLConnection
的缓存,是在 JreMemoryLeakPreventionListener
中完成的,关键代码如下所示:
// dummy.jar 不存在也没有关系
URL url = new URL("jar:file://dummy.jar!/");
URLConnection uConn = url.openConnection();
可能有些童鞋会有疑问,tomcat 只是针对该 URLConnection
对象禁用了缓存,而其它的 URLConnection
资源缓存未必被禁用啊。答案是肯定的,因为 URLConnection
的 defaultUseCaches
属性是静态变量
类加载器泄露
其中一种 JRE 内存泄露是因为上下文类加载器导致的内存泄露。某些 JRE 库以单例的形式存在,它的生命周期很长甚至会贯穿于整个 java 程序,它们会使用上下文类加载器加载类,并且保留了类加载器的引用,所以会导致被引用的类加载器无法被回收,而 tomcat 重加载 webapp 是创建一个新的类加载器来实现的,旧的类加载器无法被 gc 回收,致使其加载的 Class 也无法被回收,导致内存泄露。
DriverManager
就是典型的例子,它利用 jdk 提供的 SPI 机制加载 java.sql.Driver
驱动,而 jdk 提供的 SPI 机制便是使用上下文类加载器加载 Class 的,如果这类 jdbc 驱动由 ParallelWebappClassLoader
类加载器加载的话,就会导致该 ClassLoder
无法被回收,自然会出现内存泄露
我们来看看 tomcat 是怎么解决的?tomcat 是利用 LifecycleListener
处理 before_init
事件,将上下文类加载器置为系统类加载器,并且完成驱动的加载过程,最后,为了不影响其它的类加载,再将上下文类加载器重置为 ParallelWebappClassLoader
另外一种 JRE 内存泄露是因为当前线程会启动另外一个线程,这个时候新线程会引用当前线程的上下文类加载器,如果新线程无止尽地运行,那么上下文类加载器就会一直被引用,而无法被回收,导致内存泄露。sun.awt.AppContext.getAppContext()
便是典型的例子,它会在内部开启一个 AWT-AppKit
线程,直到图形化环境准备就绪,例如 ImageIO.getCacheDirectory()
、java.awt.Toolkit.getDefaultToolkit()
针对这种情况,解决思路也是一样的,只需要将当前上下文类加载器指定为系统类加载器即可,关键代码如下所示:
JreMemoryLeakPreventionListener.java@Override
public void lifecycleEvent(LifecycleEvent event) {if (Lifecycle.BEFORE_INIT_EVENT.equals(event.getType())) {ClassLoader loader = Thread.currentThread().getContextClassLoader();try {// 当线程上下文类加载器指定为系统类加载器Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());if (driverManagerProtection) {DriverManager.getDrivers();}// 避免开启的子线程持有 ParallelWebappClassLoader 引用if (appContextProtection && !JreCompat.isJre8Available()) {ImageIO.getCacheDirectory();}if (awtThreadProtection && !JreCompat.isJre9Available()) {java.awt.Toolkit.getDefaultToolkit();}// 避免持有 ParallelWebappClassLoader 引用if (tokenPollerProtection && !JreCompat.isJre9Available()) {java.security.Security.getProviders();}// 忽略若干代码......} finally {// 再重置为 ParallelWebappClassLoader,避免影响其它的类的加载Thread.currentThread().setContextClassLoader(loader);}}
ThreadLocal 对象泄露
还有一种内存泄露是由于 ThreadLocal
引起的,假如我们在 ThreadLocal
中保存了对象A,而且对象A由 ParallelWebappClassLoader
加载,那么就可以看成线程引用了对象A。由于 tomcat 中处理请求的是线程池,意味着该线程会存活很长一段时间。webapp 热加载时,会重新实例化一个 ParallelWebappClassLoader
对象,如果线程未销毁,那么旧的 ParallelWebappClassLoader
也无法被回收,导致内存泄露。
解决 ThreadLocal
内存泄露最好的办法,自然是把线程池中的所有的线程销毁并重新创建。这个过程分为两步,第一步是将任务队列堵住,不让新的任务进来,第二步是将线程池中所有线程停止。
tomcat 解决该 ThreadLocal 对象泄露问题,也是借助了 Lifecycle
完成的,具体的实现类是 ThreadLocalLeakPreventionListener
,它会处理 Lifecycle.AFTER_STOP_EVENT
事件,并且销毁线程池内的空闲线程,关键代码如下所示:
相关文章:
Tomcat源码分析-关于tomcat热加载的一些思考
在前面的文章中,我们分析了 tomcat 类加载器的相关源码,也了解了 tomcat 支持类的热加载,意味着 tomcat 要涉及类的重复卸装/装载过程,这个过程是很敏感的,一旦处理不当,可能会引起内存泄露 卸载类 我们知…...

DataWhale 大数据处理技术组队学习task4
五、分布式并行编程模型MapReduce 1. 概述 1.1 分布式并行编程 背景:摩尔定律已经开始逐渐失效,提升数据处理计算能力刻不容缓。传统的程序开发与分布式并行编程 传统的程序开发:以单指令、单数据流的方式顺序执行,虽然这种方式…...

Oracle 12C以上统计信息收集CDB、PDB执行时间不一致问题
文章目录前言一、统计信息窗口期调查二、时区调查三、查询alert记录四、why Database Statistic Collection Job is running two times inside a Maintenance Window?五、Default Scheduler Timezone Value In PDB$SEED Different Than CDB六、总结前言 在实际工作中发现一个…...

用Python获取弹幕的两种方式(一种简单但量少,另一量大管饱)
前言 弹幕可以给观众一种“实时互动”的错觉,虽然不同弹幕的发送时间有所区别,但是其只会在视频中特定的一个时间点出现,因此在相同时刻发送的弹幕基本上也具有相同的主题,在参与评论时就会有与其他观众同时评论的错觉。 在国内…...

算法训练营 day55 动态规划 买卖股票问题系列3
算法训练营 day55 动态规划 买卖股票问题系列3 最佳买卖股票时机含冷冻期 309. 最佳买卖股票时机含冷冻期 - 力扣(LeetCode) 给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。 设计一个算法计算出最大利润。在满足以下…...

电商共享购模式,消费增值返利,app开发
在当今以市场需求为主导的数字经济时代,消费者需求呈现出精细化管理和多元化的特性,目标市场日渐完善,另外在大数据技术迅速进步和运用的驱动下,总体行业的发展节奏感也在不断加速。因而,企业需要建立一套灵活多变的经…...

机房信息牌系统
产品特色: 无线低功耗安装简单,快速布置易于维护墨水屏显示,清晰,更环保信息后台推送,远程管理多模版样式随意制作多尺寸:4.2寸,7.5寸,10.2寸4.2寸7.5寸10.2寸标签特性:…...

金测评 手感更细腻的游戏手柄,双模加持兼容更出色,雷柏V600S上手
很多朋友周末都喜欢玩玩游戏放松一下,在家玩游戏的时候,PC是大家常用的平台,当然了,玩游戏的时候用键鼠的话,手感难免差点意思,还是要手柄才能获得更好的体验。我现在用的是雷柏V600S,这是一款支…...

Windows10 下测试 Intel SGX 功能
文章目录参考文献系统要求一、安装Open Enclave SDK 环境(一)什么是Open Enclave SDK(二)启动SGX功能方法一: BIOS启动方法二:软件方式启动(三)安装必要环境(1࿰…...

Tina_Linux_功耗管理_开发指南
Tina Linux 功耗管理开发指南 1 概述 1.1 编写目的 简要介绍tina 平台功耗管理机制,为关注功耗的开发者,维护者和测试者提供使用和配置参考。 1.2 适用范围 表1-1: 适用产品列表产品名称内核版本休眠类型参与功耗管理的协处理器R328Linux-4.9NormalS…...
golang编译dll失败问题解决
执行go build -buildmodec-shared -o exportgo.dll exportgo.go报类似如下错误/usr/lib/gcc/x86_64-pc-msys/9.1.0/../../../../x86_64-pc-msys/bin/ld: 找不到 -lmingwex/usr/lib/gcc/x86_64-pc-msys/9.1.0/../../../../x86_64-pc-msys/bin/ld: 找不到 -lmingw32安装tdm gcc m…...

Convolutional Neural Networks for Sentence Classification
摘要 We report on a series of experiments with convolutional neural networks (CNN) trained on top of pre-trained word vectors for sentence-level classification tasks. We show that a simple CNN with little hyperparameter tuning and static vectors achieves e…...

基于SpringBoot的共享汽车管理系统
文末获取源码 开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7/8.0 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven3.3.9 浏…...

TCP三次握手
参考:4.1 TCP 三次握手与四次挥手面试题 | 小林coding TCP 头格式 我们先来看看 TCP 头的格式,标注颜色的表示与本文关联比较大的字段,其他字段不做详细阐述。 序列号:在建立连接时由计算机生成的随机数作为其初始值,…...

未来土地利用模拟FLUS模型
未来土地利用模拟(FutureLand-Use Simulation, FLUS)模型1 模型简介1.1 基于ANN 的适宜性概率计算1.2 基于自适应惯性机制的元胞自动机1.3 模拟精度评价参考流域 径流变化是 自然因素和 人为因素共同作用的结果,其中人为因素最为直接的方式就…...

压力传感器MPX5700D/MPX5700GP/MPX5700AP产品概述、特征
MPX5700系列压阻式换能器是最先进的单片硅压力传感器,可广泛用于各种应用,特别是采用A/D输入微控制器或微处理器的应用。这一获得专利的单元件传感器集合了高级微加工技术、薄膜金属化、双极工艺,能够提供精确的、与所施加压力成正比的高电平…...

taobao.trades.sold.query( 根据收件人信息查询交易单号 )
¥开放平台免费API必须用户授权聚石塔内调用 根据收件人信息查询交易单号。 公共参数 请求地址: HTTP地址 公共请求参数: 公共响应参数: 请求参数 请求示例 TaobaoClient client new DefaultTaobaoClient(url, appkey, secret); TradesSoldQueryRequest req new…...
【JavaWeb】JSON、AJAX(305-317)
305.JSON-什么是JSON JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。JSON 采用完全独立于语言的文本格式,而且很多语言都提供了对 json 的支持(包括 C, C, C#, Java, JavaScript, Perl,…...

AI入场,搜索这个“营销枢纽”有新故事吗?
哪里有内容,哪里就有搜索。 以前,互联网离我们生活很远,传统搜索与用户的距离分割,只有当用户想要了解什么,才会去使用。 如今,互联网与真实世界密不可分,加之新技术、新平台的不断涌现…...

字节在职5年,一个测试工程师的坎坷之路
几年前进入到IT行业,现在发现学习软件测试的人越来越多,今天我想根据自己的行业经验给大家提一些建议。 跟其他行业相比,做软件测试的岗位确实算是高薪职业,我们那个时候起步的工资并不高,而看现在很多毕业的学生薪资都…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...

【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...

群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...