`

JVM调优:Tomcat Context reloadabled与OutOfMemory(PermSpace)

 
阅读更多

转载自:http://ayufox.iteye.com/blog/646125

 

我们知道,Sun JVM分代垃圾回收器把堆空间分成3块:

  • Young Gen:年轻代,包括1个Eden区和2个Suvivor区,新创建的对象(大部分为短周期的对象)将进入这个区,虚拟机会频繁地对这个区进行垃圾回收。
  • Old Gen:年老代,当对象在Young Gen呆地足够久(经过几次的垃圾回收仍然存在)或Young Gen空间不足时,对象将进入Old Gen,由于一般是生命周期比较长的对象,因此虚拟机对这块内存的回收频度会比较低,一旦回收,使用的将是一个耗时的Full GC,另外,一旦堆空间不足时,虚拟机也会尝试去回收这个区。
  • Perm Gen:持久代,一些常量定义和类、方法声明及其bytecode都会放在这个区

      问题在于,Perm Gen又是什么样的回收策略呢?

      一直,我都以为Sun JVM对Perm Gen使用的是不垃圾回收的策略,这个观点起源于我之前的一个测试,使用Tomcat Context的reloadabled=”true”,而且项目的jar包比较多,则只要修改几次(事实上是3次)的类定义,然后Tomcat Reload之后,WOW,抛出OutOfMemory(PermSpace)的异常。
      最近,有个同事在做一个项目的时候也碰到这个问题,他是需要应用到IDC环境下的,动态根据配置生成一些新的jar包,在不重新Tomcat的情况下,利用Context的reload属性,自动把新生成的jar加载进来。我当时给他的答案是:

  • 可以使用JVMTI技术,JDK 6新引入的启动后Instrutment,在生成jar包之后,启动Attach到当前JVM,将旧的class卸栽掉(本来也可以使用JVM的debug模式——也是利用这种技术——但debug模式对性能影响会更大)。但限制比较大,类声明不允许发生变更(譬如不能新增属性、方法或者修改方法声明),事实上也不符合他当前的需求。
  • 学过ClassLoader的同学都知道,类定义一旦加载,即使你修改ClassLoader的加载方式,强制再加载一次,也是不行的,因为加载的类定义会被JVM Cache,是不允许重新再加载的。那么Tomcat reloadable是怎么做的呢?因为JVM识别两个类定义是否一致的方法是根据加载的ClassLoader和类全名(即包括package名和类名),那么Tomcat reloadable可以重新加载同一个类定义方法是,将context旧的WebappClassLoader丢弃,创建一个新的WebappClassLoader来达到加载新的类定义的。其实就是将context stop再start,所以代价非常昂贵。这种方式相当于在PermSpace又存放了一份jar定义,所以会导致PermSpace猛增。出现OutOfMemory(PermSpace),说明对旧的class没有回收,建议可以看看JVM的启动参数,有没有对PermSpace垃圾回收的选项。

        由于我之前错误的观点,我以为Sun JVM的PermSpace是不垃圾回收的,为了证明这一点,我查了一些资料;

JVM参考规范 写道
The method area is created on virtual machine start-up. Although the method area is logically part of the heap, simple implementations may choose not to either garbage collect or compact it. This version of the Java virtual machine specification does not mandate the location of the method area or the policies used to manage compiled code. The method area may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger method area becomes unnecessary. The memory for the method area does not need to be contiguous.


       JVM规范并没有规定实现对方法区(PermSpace)一定要垃圾回收,“simple implementations may choose not to either garbage collect or compact it”。很不爽的”may”,当然不能作为问题的答案。
       重新猜想,也许JVM的启动参数是可以指定的,确实如此,然而确是很明确地指出我的想法是错误的:

-Xnoclassgc
Disable class garbage collection. Use of this option will prevent memory recovery from loaded classes thus increasing overall memory usage. This could cause OutOfMemoryError to be thrown in some applications.

 

      也就是说,默认情况下,JVM是会对这块区域进行垃圾回收的。难道是由于Tomcat的策略导致的?我们一步一步地来进行验证
      准备工作:建立测试项目test2(引用的jar包不要太大也不能太小,我建立的项目启动后JVM的PermSpace占用大概20M),在tomcat下配置该应用的reloadabled=”true”。
       猜测1:Tomcat废弃WebappClassLoader没有被置null,导致相应的引用类都没有非卸载。验证步骤如下(Tomcat测试版本是6.0.14):
       步骤1:启动Tomcat,并启动JConsole,监控,并dump下堆内存。
       步骤2:多次修改测试代码,引起Tomcat的reload生效,每次强制在JConsole上进行GC,并dump下堆内存。
       结果如下(JConsole监控图):
 
       可以看到,PermSpace在reload的时候,内存会有一个陡增,第一个陡增没有下降下来,第二个陡增后都可以通过GC下降下来,猜测是Tomcat对一个Context,在reload后会维持两个WebappClassLoader,一个当前的WebappClassLoader和一个历史的WebappClassLoader。我们通过jhat分析dump出来的堆文件证实了有2个WebapClassLoader这种猜测

2 instances of class org.apache.catalina.deploy.NamingResources
2 instances of class org.apache.catalina.loader.WebappClassLoader
2 instances of class org.apache.catalina.mbeans.MBeanFactory

      注意:用其他的tomcat版本做了相同的测试,6.0.20版本与此版本测试结果一致,6.0.26版本结果会比较好,只维持一个WebappClassLoader,即上面的JConsole曲线第一次上升之后也可以通过GC降下来。
      猜测2:Tomcat在新的WebappClassLoader把其他类加载进来之前,不会将旧的WebappClassLoader置null。验证步骤如下(Tomcat测试版本是6.0.14):
      我们先看一下Tomcat中context reload方法,可以看到reload其实就是一次stop和一次start

Java代码 复制代码 收藏代码
  1. public synchronized void reload() {   
  2.     // Validate our current component state   
  3.     if (!started)   
  4.         throw new IllegalStateException   
  5.             (sm.getString("containerBase.notStarted", logName()));   
  6.     setPaused(true);   
  7.     try {   
  8.         stop();   
  9.     } catch (LifecycleException e) {   
  10.         log.error(sm.getString("standardContext.stoppingContext"), e);   
  11.     }   
  12.     try {   
  13.         start();   
  14.     } catch (LifecycleException e) {   
  15.         log.error(sm.getString("standardContext.startingContext"), e);   
  16.     }   
  17.     setPaused(false);   
  18. }  
    public synchronized void reload() {
        // Validate our current component state
        if (!started)
            throw new IllegalStateException
                (sm.getString("containerBase.notStarted", logName()));
        setPaused(true);
        try {
            stop();
        } catch (LifecycleException e) {
            log.error(sm.getString("standardContext.stoppingContext"), e);
        }
        try {
            start();
        } catch (LifecycleException e) {
            log.error(sm.getString("standardContext.startingContext"), e);
        }
        setPaused(false);
    }

 

       步骤1:将tomcat的JVM启动参数中设置调试模式,在StanardContext的reload的start那里设一个断点,确保start之前会停住,我们将观察这种时候进行垃圾回收能否将PermSpace空间回收
       步骤2:启动JConsole,attach到Tomcat进程上
       步骤3:修改测试代码,引起Tomcat的reload生效,在第二次reload  start停止的时候,使用JConsole强行做一个垃圾回收(注意,如果是6.0.26版本,应该在第一次reload的时候观察)
        可以看到JConsole的图如下
 
       可以看到,在start停住的时候,我们进行一个GC后,PermSpace会有一个陡降,说明在stop的时候,已经会把WebappClassLoader中的内存释放掉
       猜测3:JVM在PermSpace空间不足的时候,并不会试图先去回收PermSpace
       一般情况下,我们当然无法在start之前停住,即使在这个阶段强行做一个System.gc(),然而gc的调用无法预料的,假设在start之前PermSpace内存没有被释放,而start的时候又会在PermSpace分配大量的内存,如果此时PermSpace空间不足,是否会引起JVM对PermSpace进行垃圾回收呢?
       验证步骤如下(Tomcat测试版本是6.0.14):
       步骤1:从上面我们可以看到,2个WebappClassLoader及其他classloader加载的类占用的PermSpace空间大概是25M,因此我们在启动参数中设置PermSpace最大的空间值为26M(-XX:MaxPermSize=26MB)
       步骤2:修改测试代码,引起reload,在第2次reload的时候。WOW,OutOfMemory

Caused by: java.lang.OutOfMemoryError: PermGen space
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)

       可以看到,非常遗憾,JVM在PermSpace空间不足的时候,并不会先主动去释放PermSpace空间。
       我们可以得出如下结果:

  • 对于Tomcat6.0.20及以下版本,要保证reload的时候不出现OutOfMemory的错误,必须维持PermSpace空间为:其他loader加载的类定义占的空间+3 * 应用类占的空间(WebappClassLoader加载)
  • 对于Tomcat6.0.26及以下版本,要保证reload的时候不出现OutOfMemory的错误,必须维持PermSpace空间为:其他loader加载的类定义占的空间+2 * 应用类占的空间(WebappClassLoader加载)

 

分享到:
评论

相关推荐

    离散数学课后题答案+sdut往年试卷+复习提纲资料

    离散数学课后题答案+sdut往年试卷+复习提纲资料

    智能点阵笔项目源代码全套技术资料.zip

    智能点阵笔项目源代码全套技术资料.zip

    英文字母手语图像分类数据集【已标注,约26,000张数据】

    英文字母手语图像分类数据集【已标注,约26,000张数据】 分类个数【28】:a、b、c等【具体查看json文件】 划分了训练集、测试集。存放各自的同一类数据图片。如果想可视化数据集,可以运行资源中的show脚本。 CNN分类网络改进:https://blog.csdn.net/qq_44886601/category_12858320.html 【更多图像分类、图像分割(医学)、目标检测(yolo)的项目以及相应网络的改进,可以参考本人主页:https://blog.csdn.net/qq_44886601/category_12803200.html】

    (31687028)PID控制器matlab仿真.zip

    标题中的“PID控制器matlab仿真.zip”指的是一个包含PID控制器在MATLAB环境下进行仿真的资源包。PID(比例-积分-微分)控制器是一种广泛应用的自动控制算法,它通过结合当前误差、过去误差的积分和误差变化率的微分来调整系统输出,以达到期望的控制效果。MATLAB是一款强大的数学计算软件,而Simulink是MATLAB的一个扩展模块,专门用于建模和仿真复杂的动态系统。 描述中提到,“PID控制器——MATLAB/Simulink仿真以及性能比较与分析”表明这个资源包不仅提供了PID控制器的模型,还可能包括对不同参数配置下的性能比较和分析。博主分享的是“最新升级版框架的Simulink文件”,意味着这些文件基于最新的MATLAB版本进行了优化,确保了与不同版本的MATLAB(从2015a到2020a共11个版本)的兼容性,这为用户提供了广泛的应用范围。 标签中的“PID”、“matlab”、“simulink”、“博文附件”和“多版本适用”进一步细化了内容的关键点。这表示该资源包是博客文章的附加材料,专门针对PID控制器在MATLAB的Simulink环境中进行仿真实验。多

    MATLAB代码:考虑P2G和碳捕集设备的热电联供综合能源系统优化调度模型 关键词:碳捕集 综合能源系统 电转气P2G 热电联产 低碳调度 参考文档:Modeling and Optimiza

    MATLAB代码:考虑P2G和碳捕集设备的热电联供综合能源系统优化调度模型 关键词:碳捕集 综合能源系统 电转气P2G 热电联产 低碳调度 参考文档:《Modeling and Optimization of Combined Heat and Power with Power-to-Gas and Carbon Capture System in Integrated Energy System》完美复现 仿真平台:MATLAB yalmip+gurobi 主要内容:代码主要做的是一个考虑电转气P2G和碳捕集设备的热电联供综合能源系统优化调度模型,模型耦合CHP热电联产单元、电转气单元以及碳捕集单元,并重点考虑了碳交易机制,建立了综合能源系统运行优化模型,模型为非线性模型,采用yalmip加ipopt对其进行高效求解,该模型还考虑了碳排放和碳交易,是学习低碳经济调度必备程序 代码非常精品,注释保姆级 这段代码是一个用于能源系统中的综合能源系统(Integrated Energy System)建模和优化的程序。它使用了MATLAB的优化工具箱和SDP(半定规划)变量来定义决策变

    中国飞行器设计大赛圆筒权重文件

    中国飞行器设计大赛圆筒权重文件

    java毕设项目之ssm社区文化宣传网站+jsp(完整前后端+说明文档+mysql+lw).zip

    项目包含完整前后端源码和数据库文件 环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/idea Maven包:Maven3.3 服务器:tomcat7

    风光储、风光储并网直流微电网simulink仿真模型 系统由光伏发电系统、风力发电系统、混合储能系统(可单独储能系统)、逆变器VSR+大电网构成 光伏系统采用扰动观察法实现mppt控

    风光储、风光储并网直流微电网simulink仿真模型。 系统由光伏发电系统、风力发电系统、混合储能系统(可单独储能系统)、逆变器VSR+大电网构成。 光伏系统采用扰动观察法实现mppt控制,经过boost电路并入母线; 风机采用最佳叶尖速比实现mppt控制,风力发电系统中pmsg采用零d轴控制实现功率输出,通过三相电压型pwm变器整流并入母线; 混合储能由蓄电池和超级电容构成,通过双向DCDC变器并入母线,并采用低通滤波器实现功率分配,超级电容响应高频功率分量,蓄电池响应低频功率分量,有限抑制系统中功率波动,且符合储能的各自特性。 并网逆变器VSR采用PQ控制实现功率入网 以下是视频讲解文案: 接下来我来介绍一下 就是这个风光储直流微电网 整个仿真系统的一些架构啊 然后按照需求呢正常的讲一些 多讲一些 就是储能的这块的 还有这个并网的 三相两电瓶调的这个 并网继变器的这个模块 首先就是来介绍一下呃 整个系统的一个架构 你可以看到这个系统的架构 分别有四大部分组成 最左边的这块就是混合储能啊 这边这个是蓄电池 这个超级电容 他们都是

    ajax发请求示例.txt

    ajax发请求示例.txt

    深圳建筑安装公司“电工安全技术操作规程”.docx

    深圳建筑安装公司“电工安全技术操作规程”

    220) Vinkmag - 多概念创意报纸新闻杂志 WordPress v5.0.zip

    220) Vinkmag - 多概念创意报纸新闻杂志 WordPress v5.0.zip

    智力残疾评定标准一览表.docx

    智力残疾评定标准一览表.docx

    MDIN380 SDI转VGA 转LVDS VGA转SDI 高清视频处理 MDIN380芯片 PCB代码方案资料 3G-SDI转VGA ?3G-SDI转LVDS ?高清视频 MDIN380、GV76

    MDIN380 SDI转VGA 转LVDS VGA转SDI 高清视频处理 MDIN380芯片 PCB代码方案资料 3G-SDI转VGA ?3G-SDI转LVDS ?高清视频 MDIN380、GV7601 芯片方案(PCB图和源码)。 此方案是韩国视频处理芯片MDIN380的整合应用方案。 3G-SDI转VGA或3G-SDI转LVDS。 方案共有两块电路板(一块底板,一块MDIN380核心板 四层板)。 MDIN380和GV7601 都是BGA封装,最好有焊接BGA经验才拿。 另外有视频处理方面其它需要可联系我定制开发。 其它视频格式转,视频图像分割、拼接等可定制开发。 方案资料含有源码、PCB图。 方案已有成熟产品在应用。 注意该资料没有原理图,只有PCB图。 代码环境编译KEIL4。 画图软件Protel99、AD10。 电子文档资料

    YOLO算法-锡罐-牙罐-盖子打开数据集-179张图像带标签-锡罐-牙罐-盖子打开.zip

    YOLO系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中,文件名末尾是部分类别名称; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    G120 EPOS基本定位功能关键点系列-堆垛机报F7452追踪原因.mp4

    G120 EPOS基本定位功能关键点系列_堆垛机报F7452追踪原因.mp4

    java毕设项目之ssm亚盛汽车配件销售业绩管理统+jsp(完整前后端+说明文档+mysql+lw).zip

    项目包含完整前后端源码和数据库文件 环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/idea Maven包:Maven3.3 服务器:tomcat7

    zigbee CC2530无线自组网协议栈系统代码实现协调器与终端基于GenericApp的无线收发例程.zip

    1、嵌入式物联网单片机项目开发例程,简单、方便、好用,节省开发时间。 2、代码使用IAR软件开发,当前在CC2530上运行,如果是其他型号芯片,请自行移植。 3、软件下载时,请注意接上硬件,并确认烧录器连接正常。 4、有偿指导v:wulianjishu666; 5、如果接入其他传感器,请查看账号发布的其他资料。 6、单片机与模块的接线,在代码当中均有定义,请自行对照。 7、若硬件有差异,请根据自身情况调整代码,程序仅供参考学习。 8、代码有注释说明,请耐心阅读。 9、例程具有一定专业性,非专业人士请谨慎操作。

    基于小程序的小区物业新冠疫情物资管理平台小程序源代码(java+小程序+mysql+LW).zip

    系统可以提供信息显示和相应服务,其管理小区物业新冠疫情物资管理平台信息,查看小区物业新冠疫情物资管理平台信息,管理小区物业新冠疫情物资管理平台。 项目包含完整前后端源码和数据库文件 环境说明: 开发语言:Java JDK版本:JDK1.8 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/idea Maven包:Maven3.3 部署容器:tomcat7 小程序开发工具:hbuildx/微信开发者工具

    亲测源码云赏V7.0微信视频打赏系统源码已测试完整无错版

    云赏V7.0包括V6的所有功能外,全新UI设计,代理可以选择8种风格,添加后台统计等多种功能。 1基本设置(网站基础信息配置、包括主域名、防封尾缀、url.cnt.cn短连接接口可切换); 2转跳域名(10层防守转跳,都输入的话,都会转跳到对应的地方在跳回来,在随机取用落地); 3落地域名(添加落地域名及设置默认落地域名); 4视频列表(添加视频批量添加外链视频给代理们获取); 5代理推广:代理使用推广链接发展下级代理,后台设置提成); 6代理列表(生成邀请码注册,手动添加代理); 7提现记录(用于结算代理们的提现); 8余额记录(记录代理的余额变动); 9订单记录(记录打赏数,今日收入)。 测试环境: Nginx 1.18+PHP56+MySQL5.6,详细教程见文件内文字教程。 后台账号:admin 密码:admin888

    深圳建设施工项目易燃、易爆、有毒、有害物品管理制度.docx

    深圳建设施工项目易燃、易爆、有毒、有害物品管理制度

Global site tag (gtag.js) - Google Analytics