在中间件应用服务器的整体调优中,有关于等待队列、执行线程,EJB池以及数据库连接池和Statement Cache方面的调优,这些都属于系统参数方面的调优,本文主要从另外一个角度,也就是从应用的角度来解决中间件应用服务器的内存泄露问题,从这个角度来提高系统的稳定性和性能。
项目背景
问题描述
某个大型项目(Use Case用例超过300个),在项目上线后,其Web应用服务器经常宕机。表现为:
1. 应用服务器内存长期不合理占用,内存经常处于高位占用,很难回收到低位;
2. 应用服务器极为不稳定,几乎每两天重新启动一次,有时甚至每天重新启动一次;
3. 应用服务器经常做Full GC(Garbage Collection),而且时间很长,大约需要30-40秒,应用服务器在做Full GC的时候是不响应客户的交易请求的,非常影响系统性能。
Web应用服务器的物理部署
一台Unix服务器(4CPU,8G Memory)来部署本Web应用程序;Web应用程序部署在中间件应用服务器上;部署了一个节点(Node),只配置一个应用服务器实例(Instance),没有做Cluster部署。
Web应用服务器启动脚本中的内存参数
MEM_ARGS="-XX:MaxPermSize=128m -XX:MaxNewSize=512m -Xms3096m -Xmx3096m -XX:+PrintGCDetails -Xloggc:./inwebapp1/gc.$$"
可以看出目前生产系统中Web应用服务器的内存分配为3G Memory。
Web应用服务器的重要部署参数
参数名称 |
参数值 |
参数解释 |
kernel.default(Thread Count) |
120 |
执行线程数目,是并发处理能力的重要参数 |
Session Timeout |
240分钟(4小时) |
HttpSession会话超时 |
分析
分析方法
内存长期占用并导致系统不稳定一般有两种可能:
1. 对象被大量创建而且被缓存,在旧的对象释放前又有大量新的对象被创建使得内存长期高位占用。
- 表现为:内存不断被消耗、在高位时也很难回归到低位,有大量的对象在不断的创建,经过很长时间后又被回收。例如:在HttpSession中保存了大量的分页查询数据,而HttpSession的会话超时时间设置过长(例如:1天),那么在旧的对象释放前又有大量新的对象在第二天产生。
- 解决办法:对共享的对象可以采用池机制进行缓存,避免各自创建;缓存的临时对象应该及时释放;另一种办法是扩大系统的内存容量。
2. 另一种情况就是内存泄漏问题
- 表现为:内存回收低位点不断升高(以每次内存回收的最低点连成一条直线,那么它是一条上升线);内存回收的频率也越来越高,内存占用也越来越高,最终出现"Out of Memory Exception"的系统异常。
- 解决办法:定位那些有内存泄漏的类或对象并修改完善这些类以避免内存泄漏。方法是:经过一段时间的测试、监控,如果某个类的对象数目屡创新高,即使在JVM Full GC后仍然数目降不下来,这些对象基本上是属于内存泄漏的对象了。
问题定位
这里请看5月份 Web应用服务器的内存回收图形:
《注意:5月18日早上10点重新启动了Web服务器,5月20日早上又重新启动了Web服务器。》
- 在Web应用重要部署参数中,我们知道:Session的超时时间为4个小时,我们在监控平台也观测到:在18日晚上10点左右所有的会话都过期了,从图形一中也能看出18日晚上确实系统的内存有回收到40%(就象股票的高位跳水);
- 从图形一(5月18日)中我们也能看到Full GC回收后的内存占用率走势(红色曲线),上午基本平滑上升到20%(内存占用率),中午开始上升到30%,下午上升到40%
- 从图形二(5月19日)中我们也能看到Full GC回收后的内存占用率走势(红色曲线),上午又上升到了60%,到下午上升到了70%。
- 从黄色曲线(GC花费的时间,以秒为单位),Full GC的频率也在增快,时间耗费也越来越长,在图形一中基本高位在20秒左右,到19日基本都是30-40秒之间了。
通过上述分析,我们基本定位到了Web应用服务器的内存在高位长期占用的原因了:是内存泄露!并且正是由于这个原因导致系统不稳定、响应客户请求越来越慢的。
解决方法
- 我们从图形二中发现,在8.95(将近9点钟)到9.66(将近9点40)期间有几次Full GC,但是有内存泄漏,从占用率40%上升到50%左右,泄漏了大约10%的内存,约300M;
- 我们在自己搭建的Web应用服务器平台(应用软件版本和生产版本一致)做这一阶段相同的查询交易;表明对同一个黑盒(Web应用)施加同样的刺激(相同的操作过程和查询交易)以期重现现象;
- 我们使用Jprofiler工具对Web应用服务器的内存进行实时监控;
- 做完这些交易后,用户退出系统,并等待Web应用服务器的HttpSession超时(我们这里设置为15分钟);
- 我们对Web应用服务器做了两次强制性的内存回收操作。
发现如下:
如图三所示,内存经过HttpSession超时后,并强制gc后,仍然有大量的对象没有释放。例如:gov.gdlt.taxcore.comm.security.MenuNode,仍然有807个实例没有释放。
我们继续追溯发现,这些MenuNode首先存放在一个ArrayList对象中,然后发现这个ArrayList对象又是存放在WHsessionAttrVO对象的Map中,WHsessionAttrVO 对象又是存放在ExternalSessionManager的staic Map中(名称为sessionMap),如图四所示。
我们发现gov.gdlt.taxcore.taxevent.xtgl.comm.WHsessionAttrVO中保存了EJBSessionId信息(登录用户的唯一标志,由用户id+登录时间戳组成,每天都不同)和一个HashMap,这个HashMap中的内容有:
- ArrayList: 内有MenuTreeNodes(菜单树节点)
- HashMap: 内有操作人员代码信息
- CurrentVersion:当前版本号
- CurrentTime:当前系统时间
WHsessionAttrVO这个对象的最终存放在ExternalSessionManager的static Map sessionMap中,由于ExternalSessionManager是一个全局的单实例,不会释放,所以它的成员变量sessionMap中的数据也不会释放,而Map中的Key值为EJBSessionId,每天登录的用户EJBSessionId都不同,就造成了每天的登录信息(包括菜单信息)都保存在sessionMap中不会被释放,最终造成了内存的泄漏。
如上图所示:WHsessionAttrsVO对象中除了有一个String对象(内容是EJBSessionId),还有一个HashMap对象。
如上图所示,这个HashMap中的内容主要有menuTreeNodes为key,value为ArrayList的对象和以czrydminfo为key,value为HashMap对象的数据。
如上图所示:menuTreeNodes为key,value为ArrayList对象中包含的对象有许多的MenuNode对象,封装的都是用户的菜单节点。
如上图所示,最顶层(Root)的初始对象为一个ExternalSessionManager对象,其中的一个成员变量为static (静态的),名称为:sessionMap,这个对象是singleton方式的,全局只有一个。
初步估量
我们从图形一和图形二中可以看出,每天应用服务器损失大约40%的内存,大约1G左右。
从图形四可以看出,当前用户(Id=24400001129)有807个菜单项(每个菜单项为一个MenuNode 对象实例,图形四中的这个实例的size为592 Byte),这些菜单数据和用户基本登录信息(czrydmInfo HashMap)也都存放在WHsessionAttrVO对象中,当前这个WHsessionAttrVO对象的size为457K。
我们做如下估算:
假设平均每天有4千人(估计值,这个数值仅仅是5月19日峰值的1/2左右)登录系统(有重复登录的现象,例如:上午登录一次,中午退出系统,下午登录一次),以平均每人占用200K(估计值,是用户id=24400001129 的Size的1/2左右)来计算,一天泄漏的内存约800M,比较符合目前内存泄漏的情况。当然,这种估计仍然需要经过实践的检验,方法是:当这次发现的内存泄漏问题解决后看系统是否还有其它内存泄漏问题。
方案
ExternalSessionManager类是当初某某软件商设计的用来解决Web服务器负载均衡的模块,这个类主要用来保存客户的基本登录信息(包括会话的EJBSessionId),以维护多个Web服务器之间的会话信息一致。
改进方案有两种:
-
从架构设计方面改进
实现Web层的负载均衡有很多标准的实现方式。例如:采用负载均衡设备(硬件或软件)来实现。
如果采用新的Web层的负载均衡方式,那么就可以去掉ExternalSessionManager这个类了。
-
从应用实现方面改进
保留当前的Web层的负载均衡设计机制,仅仅从应用实现方面解决内存泄漏问题,首先菜单信息不应该保存在ExternalSessionManager中。其次,增加对ExternalSessionManager类中用户会话登录信息的清除,有几种方式可以选择:
- 被动方式,当HttpSession会话超时(或过期)被Web应用服务器回收时清除相应的ExternalSessionManager中的过期会话登录信息。
- 主动方式,可以采用任务定时清理每天的过期会话登录信息或线程轮询清理。
- 采用新的会话登录信息存储方式,ExternalSessionManager的sessionMap中的key值不再以EJBSessionId作为键值,而是以用户id(EJBSessionId的前11位)代替。由于用户id每天都是一样的,所以不会造成内存泄漏。保存得登录信息也不再包含菜单节点信息,而只是登录基本信息。最多也只是保存整个系统所有的用户id及其基本登录信息(大约每个用户的登录信息只有1.5K左右,而目前这个系统的营业网点用户为1万左右,所以大约只占用Web服务器15M内存)。
- 大小: 10.1 KB
- 大小: 7.8 KB
- 大小: 19.8 KB
- 大小: 4.4 KB
- 大小: 7.6 KB
- 大小: 24.1 KB
- 大小: 24.3 KB
- 大小: 4.3 KB
- 大小: 28.1 KB
分享到:
相关推荐
利用 JProfiler 对应用服务器内存泄漏问题诊断一例 本文主要从应用的角度来解决中间件应用服务器的内存泄露问题,以提高系统的稳定性和性能。文章通过对某个大型项目的案例分析,介绍了如何使用 JProfiler 工具来...
JProfiler对应用服务器内存泄漏问题诊断解决方案 一、概述 本文主要介绍如何使用 JProfiler 对应用服务器内存泄漏问题进行诊断和解决。内存泄漏是指应用服务器中的一种常见问题,表现为内存长期不合理占用,内存...
- 在生产环境中使用JProfiler时,要注意对应用服务器的影响,最好在非高峰期进行诊断。 - 内存泄漏的检测并非一次性任务,应定期进行,尤其是当性能问题出现时。 - 结合日志和其他监控工具,可以更全面地了解问题。 ...
总的来说,JProfiler是一个强大的性能调试工具,它提供了一系列高级特性来检测和解决Java应用程序中的内存泄漏问题,同时也能帮助优化整体性能。通过熟练使用JProfiler,开发者可以更有效地诊断和修复潜在的问题,...
本文档将详细介绍如何使用JProfiler来定位和解决内存泄漏问题,旨在帮助开发者提升应用程序的性能和稳定性。 1. **内存泄漏概念** 内存泄漏是指程序在申请内存后,无法释放已申请的内存空间,一次小的内存泄漏可能...
总之,JProfiler作为一款强大的内存泄漏检测工具,通过其丰富的功能和直观的界面,可以帮助开发者高效地定位和解决内存泄漏问题,从而提高Java应用的稳定性和性能。正确使用JProfiler,可以显著提升开发者的诊断效率...
使用JProfiler监控Apusic应用服务器,不仅是性能监控的实践,更是一次对应用架构与性能优化策略的深刻反思。通过上述步骤,我们不仅能即时掌握Apusic服务器的运行状态,还能基于数据分析,制定科学合理的优化方案,...
- 内存分析是识别和解决Java应用程序中的内存泄漏、过度对象创建和内存消耗过高问题的关键步骤。 - jProfiler7提供了详细的内存分配和存活周期视图,帮助开发者定位内存占用大的对象和可能导致问题的代码片段。 2...
内存泄露是程序运行过程中,无法释放不再使用的内存...总的来说,理解和预防内存泄露是每个Java开发者必备的技能,而JProfiler作为一款强大的工具,能够有效地帮助我们诊断和修复内存问题,提高软件的稳定性和效率。
它专为Windows x64平台设计的版本是"jprofiler_windows-x64_9_1_1",这个版本提供了对Java应用程序内存使用情况的深入洞察,帮助开发者诊断和优化内存问题。 内存分析是JProfiler的核心功能之一,其内存视图提供了...
Java内存分析是一个重要的主题,尤其是在开发复杂且性能要求高的应用程序时。内存泄露是Java程序员需要密切关注的问题,因为它们可能导致系统性能下降,甚至可能导致系统崩溃。本文将深入探讨Java内存分析和内存泄露...
java检测内存泄露工具--jprofiler5 里面含破解注册码,按照使用说明即可使用!
它把 CPU、执行绪和内存的剖析组合在一个强大的应用中,提供许多 IDE 整合和应用服务器整合用途。jprofiler 的主要功能包括内存剖析、CPU 剖析、线程剖析和 VM 遥感勘测等。 内存剖析是 jprofiler 的一个重要功能,...
1. **内存分配和泄漏检测**:JProfiler11能详细追踪对象的创建、存活和销毁过程,帮助识别内存泄漏。通过查看对象分配图表,可以发现哪些类或方法在消耗大量内存,从而定位潜在的问题。 2. **垃圾收集分析**:...
JProfiler是一款强大的Java剖析工具,它集成了CPU、线程和内存的分析,适用于J2SE和J2EE应用程序。通过其直观的GUI界面,开发者能够定位性能瓶颈,检测内存泄漏,解决线程问题,以及进行heap分析。JProfiler还提供了...
**JProfiler**是一款强大的Java应用程序性能分析工具,主要用于检测和优化Java应用的内存使用、CPU性能、线程活动、锁竞争、SQL查询性能等问题。它提供了丰富的可视化界面和详细的统计信息,帮助开发者深入理解并...
JProfiler既能在本地启动应用程序进行实时监控,也能连接到远程服务器,使得开发者能够远程分析生产环境中的性能问题,而无需中断服务。 7. **应用实践** 创建一个模拟负载环境,使用JProfiler监控内存使用情况、...