当前位置: 代码迷 >> Web前端 >> IBM WebSphere Application Server 确诊和调试
  详细解决方案

IBM WebSphere Application Server 确诊和调试

热度:502   发布时间:2012-12-23 11:28:15.0
IBM WebSphere Application Server 诊断和调试

近段时间,我们项目中用到的WebSphere 应用服务器(WAS) ,但在客户的production 环境下极不稳定,经常宕机。给客户造成非常不好的影响,同时,也给项目组很大 压力。为此,我们花了近一个月时间对其诊断,现在基本上稳定了,需要继续观察一段时间。现在我主要将工作做一个阶段性的总结。
我们的产品环境是:WAS6.0DB2 8.1 +AIX5.3+RS/6000 。在该产品环境下,出现的问题非常多,现象如下:
WAS经常不稳定、 机几乎一天一次,经常报告OutOfMemory ( 内存泄漏吗?NO)。
DB2
连接数过大,有时把DB2 撑死,有时也把AIX 撑死。
AIX虚拟内存报错、分页报错IO 也报错、还有很多其它莫名奇妙的错。

总是,每次问题发生的现象和理论上的总是不一致,导致我们不知道从何入手,也无从检测自己的优化参数。咨询过多次IBM 技术支持,只解决了某些局部问题。
虽然问题依然存在,但我想,解决问题的思路、特别是理论基础,还是有一些规律和原则。

对于WAS 这块,我近段时间的主要时间集中在以下几个方面( 时间顺序)
1
Java 性能监测工具:Jprofiler ,也用到Jprobe 。后来发现JprofilerAIX 下几乎不可用。
2
IBM Java 虚拟机和WAS 技术细节,特别是IBM JVM的GC 原理,我发现它和sunbea 的差别很大。
3
IBMheap 分析器Heap Analyzer、GCCollector 。这两个事后监测工具非常实用,特别是我们的产品运行环境,非测试环境。
4、某些Application 的怀疑和诊断。
5
AIX 诊断,我几乎没有这个能力,只能常规监测一下,需另请高人。

我打算将本文分成以下几个部分总结:
JVM
原理、IBM JVMGC 策略和调优。
Jprofiler
IBM 工具的实际体会
WAS的诊断体会和AIX 调优

下面开始主题吧,可能比较零碎,另外,开始的理论篇基本上看书都可以,我只是总结一下,再添加一些自己的理解。

以下是我参考的最重要的两本电子书和一些网站:
Inside Java Vrtual Machine 》:
半部分有约四章我认为非常 棒,其它章节可能意义不大。
The Java Virtual Machine Specification, 2nd 》:前半部分有两三章很不错,不过可以对照上一本书看。
sun
hotspot 虚拟机技术:http://java.sun.com/javase/technologies/hotspot/
BEA
JRockit 虚拟机技术:http://edocs.bea.com/jrockit/geninfo/genintro/index.html JVM技术文档入口,虚拟机理论,内存泄漏诊断等的索引页。
IBM诊断资料:http://www-128.ibm.com/developerworks/java/jdk/diagnosis/ 上面有一个500 多页的pdf 文档,对IBM JVM技术和诊断讲解很深入。

我不得不提的是,在查资料这块,BEASun 都有很好的官方文档和论坛支持,并且官方文档导航非常好。虽然IBM 的诊断资料也不少,但需要搜索,其搜索是很痛苦的。而且,IBM 官方论坛很差。如果用IBM的产品出问题,切记:找IBM 技术支持,千万不要蒙着头搞!反正它们的产品很少免费。说实话,它们的技术支持还是挺负责的,一般会为你推荐很多support资料,而该资料往往都在developerworks 网站上,属于support那个频道,但你就是搜不着。

Java 虚拟机规范概要
研究Java 虚拟机,首先要了解SunJava 虚拟机规范。现在,该实现版本很多,如比较有名的Sun、IBMBEA 、Apple、HPMS 、Apache Harmony。它们都实现了JVM 规范,但有各自扩展。譬如,针对IBM虚拟机的堆碎片导致OutOfMemory (OOM),在Sun 的虚拟机上就不会发生。Sun 的JVM有maxPermSize 的概念,IBM就没有,如果你设置这个参数,虚拟机根本就启动不了。
比较有意思的是,学Java ,就一定要了解各种规范,这和MS 的风格很不一样。Sun总是在定义一些规范,实现都留给各厂商。我们除了理解规范本身外,一定要理解规范和实现之间的关系,譬如JDBC规范和JDBC 驱动的关系,它们是怎么组合到一起的。要是你用过phpxml 解析库,或db函数,就会体会深刻,它们可没有什么规范可言,所以每个数据库厂商的db 函数用法都不一样。我推荐大家研读一下HSQLDB的jdbc 和Tomcat的servlet 相关实现,因为我认为它们还是比较好懂的。
JVM规范只是定义一个虚拟机该做什么,但它并没有要求你该怎么做。例如我们最常见的Servlet 规范,在该规范中,有HttpServletRequest HttpServletResponseHttpSession 等接口,但它们的实现都留给了各个容器厂商。遗憾的是,规范留下的空白,会把我们这些开发人员给整惨了:容器间移植有时候就是恶梦。譬如J2EE 并没有SSO 规范,但它很重要,我以前专门针对它做过WebSphere AppServerWeblogic AppServerSSO 项目,差别还是不小,不过还是有点共通,那就是都遵循JAAS规范。

JVM 的结构
从功能上分,Java 虚拟机主要由六个部分组成,可以分成三类:
第一类:JVM API :就是我们最常用的Java API ,它是开发人员和Java交互的入口,它主要是JAVA_HOME/jre /lib 下的运行时类库rt.jar 和编译相关的tools.jar

第二类:JVM 内部组件
类装载器 (ClassLoader ) :将Byte Array
.class 文件装载、链接和初始化。
内存管理 (Memory Managent ) :为对象分配内存,以及释放内存。后者就是垃圾回收Garbage Collector(GC )。由于JVM最复杂的、最影响性能的就是GC ,所以内存管理一般就指垃圾回收。
诊断接口 (Diagostics Interface) :这主要体现在JVMTI(jdk1.4 下的JVMPI 和JVMDI),它主要用来诊断程序的问题和性能,一般提供给工具 厂商实现。如eclispe IDE 下的debug功能,Jprofiler 性能调优工具。
类解释器 (Interpreter) :解释装载进虚拟机的class对象,包括JIT 等特性相关。

第三类:平台相关接口(Platform Interface) :主要为了跨操作系统平台重用JVM 代码,不过,它和我们开发人员关系不大。

在以上六个组件中,我们开发人员最关心的是ClassLoader GC ,用Java 做系统框架、容器和它们密切相关。做业务系统时一些基础代码也和它们打交道,譬如最常用的Class.forName (),Thread.currentThread.getContextClassLoader ()。我们仔细想想,为什么是上面两个问题?因为,它和我们class的整个生命周期最为相关:怎么将一个class 和相关class加载进来,class 实例什么时候创建,什么时候被销毁?
所以,下面的部分我们要专门讨论这些问题。

ClassLoader
JVM主要有三类ClassLoader BootstrapExtentionApplication ,该三类ClassLoader 从上到下是分级(hierarchy) 结构,遵循代理模型(Delegation Model)。
Tip
:大家可以看看sun.misc.Launcher 的源码,Bootstrap和Extention 就在该文件里。该src 可以在sun 的网站上下载该压缩包,约60M (jdk-1_5_0-src-scsl.zip),它不在jdk 自带的那个src.zip 里。

Bootstrap ClassLoader :也称为primordial(root) class loader 。主要是负责装载jre /lib 下的jar文件,当然,你也可以通过-Xbootclasspath 参数定义。该ClassLoader 不能被Java 代码实例化,因为它是JVM本身的一部分。

Extention ClassLoader :该ClassLoader 是Bootstrap classLoader 的子class loader。它主要负责加载jre /lib/ext/下的所有jar 文件。只要jar包放置这个位置,就会被虚拟机加载。一个常见的、类似的问题是,你将mysql 的低版本驱动不小心放置在这儿,但你的Web 应用程序的lib下有一个新的jdbc 驱动,但怎么都报错,譬如不支持JDBC2.0的DataSource ,这时你就要当心你的新jdbc 可能并没有被加载。这就是ClassLoaderdelegate 现象。常见的有log4j、common-logdbcp 会出现问题,因为它们很容易被人塞到这个ext 目录,或是Tomcat下的common/lib 目录。

Application ClassLoader :也称为System ClassLoaer 。它负责加载CLASSPATH环境变量下的classes 。缺省情况下,它是用户创建的任何ClassLoader 的父ClassLoader ,我们创建的standalone 应用的main class缺省情况下也是由它加载( 通过Thread.currentThread ().getContextClassLoader ()查看)。
我们实际开发中,用ClassLoader 更多时候是用其加载classpath 下的资源,特别是配置文件,如ClassLoader.getResource (),比FileInputStream 直接。

ClassLoader 是一种分级(hierarchy ) 的代理(delegation )模型。
Delegation
:其实是Parent Delegation ,当需要加载一个class时,当前线程的ClassLoader 首先会将请求代理到其父classLoader ,递归向上,如果该class已经被父classLoader 加载,那么直接拿来用,譬如典型的ArrayList ,它最终由BootstrapClassLoader 加载。并且,每个ClassLoader 只有一个父ClassLoader
Class查找的位置和顺序依次是:Cacheparent 、self。
Hierarchy
:上面的delegation 已经暗示了一种分级结构,同时它也说明:一个ClassLoader 只能看到被它自己加载的classes,或是看到其父(parent) ClassLoader 或祖先(ancestor) ClassLoader 加载的Classes。
在一个单虚拟机环境下,标识一个类有两个因素:class 的全路径名、该类的ClassLoader

我碰到的一个典型的例子是:在做WASSSO 开发时,由于我们的类是由WAS 在启动时加载,该ClassLoader 比下面的部署的ApplicatonClassLoader 的级别高。所以,在我们自己的类中没法用到应用程序的连接池,必须自建。
代理模型是Java 安全模型的保证。譬如,我们自己写一个String.java ,并且编译、package 到自己的java.lang 包下。按照代理模型,当前线程的ClassLoader 会将其代理到父ClassLoader ,父ClassLoader ( 最终会是Bootstrap)会找到rt.jar 下的String.class ,也就是说我们的String.class 不会捣乱。

自定义ClassLoader
我们前面说过,自定义ClassLoader 的缺省父ClassLoader 是Application ClassLoader 。一般的应用开发用不到它,但我们最好理解。因为在内存泄漏查找、应用程序部署出问题时,很多都和它有关。
譬如,内存泄漏是怎么产生的?这就涉及到ClassLoader 和Class的生命周期。我曾经碰到这样一个问题:我们的程序用到了WebworkSpring 框架,当部署到Tomcat 下时没有任何问题,但部署到WAS下,报告找不到Webwork 的xml的DTD 文件,而且Spring 的日志也总是失效。Why?因为解析xml dtd 时,用的是IBM的Xerces ,不是我们的。而Spring日志问题是因为应用程序用的是WAS 的Common-log.jar ,而不是我们的。将应用的ClassLoader 从默认的Parent-First ,改成Parent-Last就可以解决,不过我们项目中用到其它库,又发生了其它问题。

一般来说,用到自定义ClassLoader 有三种情况:
1
、应用框架可以自己控制Classes 的目录,并且自动部署。
我读过Jive 公司的Wildfire( 著名的即时通讯服务器),它自己有一套应用框架,非常灵活,遵循该框架插件规范的 第三方的plug-in放置在指定目录可以自动部署,实现某些扩展功能,如文件传输、语音聊天。
2
、区分用户代码
这被广泛应用在Servlet 容器和类似容器,譬如EJB Container设计中,大家看到Tomcat 下有common、servershare 三个目录吧(ClassLoader 顺序从左到有) ,另外也有用户应用的WEB-INF目录,它是我们自己开发的。
3
、允许Classes 卸载
如果没有自定义的ClassLoader ,那么我们自己应用中的classes永远都不能被卸载,因为这些类被Application ClassLoader 加载后cache 起来了,我们的classes一直对该ClassLoader 有引用,而该系统级的ClassLoader 永远都不会被卸载,除非JVM shutdown了。JSPServlet 的动态部署就用到这个特性。

Note: 还有JVM 运行时(Runtime) 架构,ClassLoader 加载class 过程没有总结,这两部分我觉得太重要了,但内容太多,写不完啊。

??? 这部分内容,《Inside Java Virtual Machine 》讲解非常清楚,BEA的官方网站这部分也非常不错,要理解深刻,我建议结合JProfiler 工具,非常直观。

?

续写这篇文章,已经过去一个半月了。直到现在,系统一直运行平稳。
先说说我接手这项工作的经历吧:该项目大部分是0610 月就部署在客户那边了,到07年3 月份,WAS 宕机问题实在无法忍受,我才加入进来,前半年有另外一位同事断断续续处理,但对问题一直都无可奈何,而且项目负责人也没有引起足够的重视。可想而知,最后付出的代价是非常惨重的。在这近半年的时间内,服务器宕机63次。每次宕机时,WASJVM 会dump出一个heapdump.phd 文件(heap快照) ,然后JVM 就死掉了,当然,此时WAS也停止了响应。一般我们的做法是重启,最后是干脆AIX 每天晚上定时重启。有时候一天还死多次。大家见附件的截图(all-GC.png)。这是我接手后,用IBM 的分析工具得到的截图。对截图的分析,留给后面对应的部分吧。
服务器不稳定、宕机问题,拖延到最后,客户愤怒了,公司高层也害怕了,部门还专门成立了八人攻关组。当然了,我当时的压力也非常大,因为我是技术负责人,也就是实实在在干活、想主意的。
服务器诊断那段时间,从前到后,我们也是沿着一条线走下来,虽然最后发现很多路都走不通。现在就按这个思路,也就是时间先后一步步叙述吧。我想,大家如果也碰到类似应用服务器诊断,应该思路差不多。

术语说明:
IBM Websphere Application Server
WAS ,WebSphere本身是一个平台,产品家族
OutOfMemoryError
OOM ,内存泄漏,内存溢出
Gabage Collection
GC ,自动垃圾回收
Content Management System
CMS ,就是给新闻类门户网站编辑们用的系统

我们诊断大体上经历了以下几个阶段:
1
、按Job 调度线程池引起内存泄漏诊断:因为很多次OOM 是发生在某个特定时候,譬如14:3022 :40左右。
2
、按应用程序引起内存泄漏诊断:用JProfiler 等工具探测:因为总是发生OOM。
3
、分离WAS 怀疑有OOM 的应用:因为每个WAS应用太多,20 来个,混一起没法定位。
4、用IBM 官方heap 、GC分析工具。以及和IBM 技术支持联系。WAS 、AIX参数优化。
5
、隔离出WAS 超级恶魔程序:一个CMS 产品。
6、WASAIX 参数优化、设置。

我们走到第5 步时,才出现效果。计算一下,那时已经过去一个月了。服务器宕机、系统不稳定,在这个验收的时候,客户已经忍无可忍,以致后来的每一次行动都得胆战心惊得去做。

一、按Job 调度线程池导致内存泄漏诊断
因为从我们WAS 的日志( 默认是native_stderr.log) 来看,最近半年的宕机时间都有一个明显时间规律。见附件截图Job1-1.png。
我想,做过Java 服务器性能调优的朋友,都知道在Web 容器里面启线程池是个不太好的做法,因为Web容器本身有一个线程池,譬如Servlet 线程池(Tomcat默认起25 个),而自启的线程池很容易导致Servlet线程管理混乱,最终导致GC 问题。我们的现象似乎和那很符合。如果我们沿着这个思路做下去,具体怎样实施呢?

我们的WAS 上部署了20 个左右的Web 应用,譬如Lucene全文检索、B2B 行业数据同步等,都是通过Quartz的Job 调度做的,当然还有很多其它调度。当时,由我负责,通知相关负责人,将定时调度暂时去掉。观察了几天,后来发现问题依然存在,不过时间有点随机了。
不过,最后还是发现OOM 不是由Job 调度引起的。
也就是说,我们这个方案是失败的。而且,我们的很多想法都是臆测的,没有可靠的根据,也没有方向,再加上我是第一次处理这种问题,这导致后来查找问题的艰难。但是,仔细想想,我们又能拿什么做依据呢?出现OOM错误,我想大多数人想到的,除了JVM 参数设置,就是内存泄漏。其实,OOM发生有很多种情况,在IBMSun 、BEA等不同虚拟机上,因为GC 机制不一样,所以原因一般都不同,容易定位难度也不一样。下文会谈到。
于是,我们干脆釜底抽薪分析问题吧:用JProfiler 检测。

二、按应用程序导致内存泄漏诊断,JProfiler 检测
如果遇到OOM 问题,我想大家都会想到内存检测工具,现在最可靠的还是下面三种分析工具:Borland Optimizeit Suite ,Quest的JProbeej-technologies 的JProfiler。但面临三个问题:
1
、三个都是商业产品,公司暂时没有买,必须自己下载,而且要找序列号。
2
、工具必须支持AIX5.3JDK1.42 +WAS6.0,不是Windows 平台。
3
、工具必须在客户真实环境下部署,对客户的业务不能有冲击,也就是说部署测试工具前,必须做个大量测试,对工具非常熟练,遇到意外可以立即恢复现场。
Note:项目上线后,而不是测试或试运行阶段遇到此类问题,非常考验人;另外一个,就是性能和可伸缩性问题,很可能把整个项目给毁了。

当我决定要这么做后,就立即动手查阅这些工具的官方文档,用emule 下载,最终都下载到了。试用后,最终选择了JProfiler4.03 ,比起其它工具,它界面美观、清晰、功能强大、集成度高(Heap,Memory,CPU,Thread都统一了) 。另外,JProbe没有AIX 版本,这也是放弃它的一个原因。

JVM Profiler 原理,都是通过JVM 内置的的标准C 语言Profiler 接口收集数据,然后通过Profiler 工具的客户端展现。也就是说各厂商的Profiler工具,都有两个部分,一个部分是Profiler Agent ,和JVM绑定,负责收集JVM 内部数据,譬如方法调用次数、耗费时间等;另外一个部分就是Profiler front-end。通过Profiler 工具的自定义local或remote 协议传输到front-end ,其实,我们最常用的JavaIDE的debug 功能就是在此基础上的(JPDA)。(JProfiler 的截图http://www.ej-technologies.com/products/jprofiler/screenshots.html )。
下面是Sun 官方文档:
JDK1.42
及以前是JVM PIhttp://java.sun.com/j2se/1.4.2/docs/guide/jvmpi/jvmpi.html
JDK1.5
JVM TIhttp://java.sun.com/j2se/1.5.0/docs/guide/jvmti/jvmti.html
具体到JProfiler 的配置上,专门针对JDK1.4 和1.5的JVM 配置差别很大。

我用的JProfiler4.31 版本,透露给大家一个万能序列号吧(这东西不太好找) ,对各版本应该都支持。深入了解Java,这类工具是不可少的:
Name: License for You
Lincese Code: A-G667#42616F-10kg1r134fq9m#2217

为了保证真实环境的检测成功,我做了大量的试验,譬如:
1
Windows 系列的本地、远程测试。
2
AIX 的远程测试。
3
Tomcat5.0WebLogic8.14 、WebSphere6.02,以及上述两种方式的组合测试,排列组合,场景不下10 个。
当时也参阅了大量JVM 文档,JProfiler 官方几百页英文文档,辅助的JProbe对照。而且也制造过内存泄漏造成的OOM 场景。
当然,要是在几个月前,在客户那边部署的测试环境时,就进行测试该多好啊。

在公司内部,我用JProfiler 测试了我们当时部署的几个应用,没有发现内存泄漏,所以,我们最怀疑的是就是CMS系统。因为出问题的那个WAS 上它占去了90%的负荷(我们有多台AIXWAS 服务器)。该CMS超级庞大,感觉著名的赛迪网就用它,当时该CMS 厂商给我们部署都花了快一个月。所以再重新部署一套测试环境也挺困难。另外,CMS提供商不给lisence 。现在测试,客户早就对我们恼火了,当然不怎么配合,这对我们工作的开展就有很大的挑战。

在大致可以确定万无一失的情况下,我们最终决定在客户的真实环境下测试。也就是让JProfileragent 端直接在WAS 的JVM里面启动(北京IDC ),然后远程(大连)监控。
本来该模式在另外几个应用的测试都通过了(因为北京IDC 那边好几台AIX服务器)。但一部署上,客户的一些编辑用CMS 时就感觉超级慢,尽管我们用了JProfiler的最小负载模式。半个小时后,客户实在无法忍受,打电话过来,又把我们部长和经理训了一顿,还要写书面报告。我们被迫马山中止测试,恢复现场。

虽然JProfiler 也支持客户那边的环境,但还是有bug ,至少负载一高就有严重的性能问题,几乎导致系统挂起,这是我当时没有料到的。JProfiler一启动,WAS 的启动时间就由原来的3分钟降到10 分钟,而且系统响应明显变慢,我们具体的环境如下(排列组合恐怕不下20种):
1
AIX5.3Power PC64 位(不是32位)
2
WebSphere6.0
3
IBM JVM1.42
4
Remote 模式

我后来仔细读了一下JProfilerchangeLog ,发现对上面的环境支持不够也很正常,因为官方在最近的4.3.x版本下陆续有对IBM JVMWebsphere6.0 的features和bug fixhttp://www.ej-technologies.com/download/jprofiler/changelog.html

进行到这一步,我忽然觉得无计可施了 ,此时已经过了三周。
上面的策略,我认为是很正统的处理方法。谁怪我们当初项目上线时没有进行充分的测试呢?其实,这类测试没做也正常,OOM 这类问题谁都无法预测。

到这个时候,我想肯定有人会问我?你知道导致JVMOOM 有几种情况吗?在当时,我想到了以下五种:
1、JVMheap 最小、最大值没有设,或不合理。
2、JVMmaxPermSize 没有设置(这个IBM的JVM 没有,一设置JVM 就起不来)。
对于SunBEA 的JVM,以上两种参数设置,可以排除90 %以上的非程序内存溢出导致的OOM。
3
、程序内存泄漏
4
、有的JVM ,当在80% 的CPU时间内,不能GC2% 的heap时,也发生OOMIBM 的JVM就是,但我没有验证)
5
JVM 本身内存泄漏(JVM 有bug不是不可能)

现在的难题是,如果是那个可怕的CMS 程序本身有内存溢出,在产品环境下,我们怎么去验证?我们自己开发的10 来个web应用,测试并不是很难,在公司测试都可以。但是,我现在最想解决的,就是CMS 测试的问题。而且,在我们那种环境下,该CMS产品供应商并没有透露成功案例。

其实,最后发现,并不是内存泄漏造成的,因为我们的heap 走势都很平稳。纳闷的是,有1000Mheap ,每次在heap只被占用400M 时就发生OOM,没有任何预兆。大家猜猜,会是什么原因 ?这个问题我到后面相关章节再说吧。

既然我们所有的矛头都指向那个可怕的CMS 系统,现在就是考虑隔离的问题。如果我们验证这个问题是CMS 造成的,那么大部分责任就应该由CMS厂商承担
既然CMS 我们不敢移(费劲,而且客户在正式用),那我就移我们开发的10来个web 系统吧。

三、移出除CMS 系统以外的所有应用
说起来容易啊,做呢? 隔离(移动)工作由我负责,具体涉及到10 来个相关负责人。
转移工作,必须处理好很多问题,就说几个印象最深的吧:
1
、某些应用,如BlogBBS ,都涉及到文件、图片上传目录和产品本身的环境,如 JDBC连接池、Cache 位置。
2
、目标服务器本身的环境,WAS 安装环境、网络等。
3
、移植时的先后顺序、调度,各应用内部本身的约束关系。
4
、移植后的测试。
当然,还有一个最严峻的问题,客户允许我们这么做吗?对它们目前运行的系统有多大影响?风险如何评估?

这个工作持续了一天,已经完成了80 %的工作,到第二天,客户又恼火了:WAS 又宕机了。
为什么?这确实是WAS 的一个bug :WAS的后台随便一操作,heap 就会突然上升几百M,导致JVM 内存不够。不过WAS 撑住的话,过半小时后就会降下来,我估计是WAS后台对用户操作状态、文件都缓存到Session 里面。你们可以检查类似这样的一个文件夹:d:\IBM\WebSphere\AppServer\profiles\AppSrv01\wstemp,我不知道为什么WAS不主动去清除它,它偷偷的就上升到几个G ,系统硬盘可能不久就后就会空间不足,WAS莫名迟缓、最后死掉。听过WAS6.0 以前这个目录更夸张。大家见我附件的截图WAS_Console.png那个尖峰。

咋办?经理也已经不敢让我们继续铤而走险了。这个方案最终又以失败告终

不过,最后我们还是发现问题出在CMS 上。我们以前把这个问题向CMS 技术支持反映,有大量依据和现象,并且把相关日志都给它们。过了两天,他们最后竟然只回了一句话“从给我的两个日志来看,没有找到任何与XXX 有关的东西....”。TMD !我真的很生气 ,它们的产品都折磨我们半年之久了。不过,看他们对IBMWAS 和JVM也不懂,我也就不想再说什么了。下面是我的邮件,公司机密部分都隐去了:

引用

附件是我们这段时间服务器宕机的日志。我们用IBM PatternModeling and Analysis Tool for Java Garbage Collector Version 1.3.2 分析了一下虚拟机日志,没有发现是内存泄漏导致;用IBM HeapAnalyzer Version 1.4.4 分析heap 文件,也没有发现很可疑的内存泄漏。
我想以前你们也这样做过,现在我们分析错误日志,发现有一个现象,在宕机时,总是找不到文件,我看就是Websphere 或是AIX IO资源不够,不知道是什么导致的。但是,我们自己的应用,基本上没有什么IO ,除了一次load几个配置文件。不过,我觉得你们WCM 的IO操作挺多的,不知道你对日志有什么新的发现。
客观的说,这几个月来,宕机那台服务器,除了你们的XXX ,就以论坛和blog为主,而且他们都是开源的。在频繁宕机的06 年11月份,我们的论坛和blog 还没有上线。现在我们不得不每天晚上11点定时重启,但这也不是长久之计。
现在,我们进行分离遇到很大阻力,原来想把你们的XXX 单独分离出来,在当前的环境下,不是很现实,如安装、测试(负载、定时服务),所以现在分离我们自己的应用,但当前在产品环境下,客户方阻力也很大。
希望尽快能够得到你们的问题建议和方案。

文中说到了IBM 的两个分析工具,这也是我们后来的救星:我们就是需要这种离线分析 工具,因为实时检测 已经证明不现实。但我始终对该分析出来的结果抱怀疑态度,直到我去深入IBM的JVM 以及和IBM 的技术支持交流......

柳暗花明啊 ,至少看到了一点希望,不过最后我们还是失望而返。

四、用IBMHeapAnalyzerGarbageCollector 检测

找到这两个工具,已经是够费劲了,因为以前找的IBM HeapRoot 工具,让我对这类工具很失望。而且,这两个工具,只有在IBM 的Techinical Support网站能够搜索到,但很不容易,因为那两个工具,并不是象IBM 的Websphere产品那样宣传,它只在IBM Techinical Support 文章的某些角落里出现。要知道,Techinical Support是IBM 很重要的收入来源,这类文档,他们并不会让你很轻易就拿到,比起BEA WLS的支持网站dev2dev 差远了。
具体诊断细节我就不详述了。我认为,IBMWAS 或JVM出了性能和OOM 问题,这两个工具是最有效的,而且是离线分析工具,比起那些实时Profiler工具,某些场合有绝对的优势:譬如我们目前的产品环境,你只能分析宕机后的日志,实时分析前面已经验证是不可行的。
从日志分析,我们最终得出结论,我们购买的CMS 系统有严重的碎片(大对象)问题,而该问题是OOM的罪魁祸首,而且IBM 工程师也得出了同一结论。不过,在起先我们得出这一结论一周后,我还始终不相信heap碎片会导致OOM ,直到IBM 工程师总是向我强调。

我想很多人也是不太相信,因为大多数人用的都是SunJVM ,譬如Windows 、Solaris上的hotspot 。而且,SunJVM 出问题,如果是配置的问题,一般通过配置heap 最大最小值,以及maxPermSize都可以解决。Heap 碎片导致的OOM,只有BEAJRockit 和IBM JVM上发生,不过JRockit 有专门文档说明,而且很容易找到(就在jdk的文档里面)。

配置heap 最小最大值 ,我想大多数人都有经验。对于SunJVM 来说,一般可以设置heap 最大最小值一致,也是推荐的做法。因为它的GC策略默认是复制、分代算法。也就是说,它会将heap 分成不同的几个区,譬如Solaris JVM中最上面有两个大小相等的区。GC 时刻,将一个区的存活对象复制到另外一个对等区,垃圾对象就算遗弃了。这样在heap里面,就不存在碎片问题。另外,根据Java 对象的存活期不同,将之转移到不同的区(Tenured区),存活最长的在最底部(火车算法),这也就是分代模型。具体请参考官方文档:http://java.sun.com/docs/hotspot/gc1.4.2/

对于maxPermSizePermanent Generation ), 主要和那些加载到JVM 里面的JavaClass 对象相关,它的空间不是在Java Heap 里面分配。如果你当前的heap有1000M ,permSize是200M ,那么JVM 至少占用1200M
在这个空间内的对象的生存期和JVM 是一样的,譬如JDK 的核心类库,它们被System Classloader加载到JVM 的Method Area(方法区)后,就不会被GC 掉的,这些对象一般是Class对象,而不是普通的实例对象,也就是JVM 的元数据。我们在用反射时经常用到它们。所以,对于现在象Spring、Hibernate 这些框架经常通过反射创建实例,可能对maxPermSize要求就大了,缺省的64M 很多时候是不够的,特别是对于应用服务器里的应用,象JSP 就会产生和加载很多classes。不过,如果是它导致的OOM ,一般会有类似 perm size提示。

但是,对于IBMJVM ,情况就完全不一样。它的默认GC策略并没有采取复制、分代。这个可以从GC 日志分析出来。它不像Sun的JVM 那样,有个单独的方法区,它的方法区就放在Java Heap里面。JVM 规范里面并没有要求方法区的必须存放的位置,因为它只是一个JVM实现问题。

在IBMJVM 里面,这些对象一般分配在称为k-cluster和p-cluster 里(cluster又是属于Heap ),而后者一般是临时在heap里面申请。并且,这些cluster 是不能GC,或是被移动重排的(Compact 过程)。这就导致Java Heap里面就如同马蜂窝,但不同的蜂孔又不能合并,于是,当我们程序里面产生一个大对象,譬如2M 的数组(数组必须分配在连续的内存区) 时,就没有可分配空间了,于是就报告OOM。这些不能被移动的cluster 就称为所谓的碎片。此时,JVM的Heap 利用率可能不到50%
当然,通过一定时期的GC 日志,可以计算出cluster 的合理大小(专门在Java Heap的底部),另外,还可以为这些大对象专门分配大对象区的(超过64k 的对象)。

通过上面的理论介绍,我想大家一定知道了为什么IBMJVM 里面不推荐heap 的最大最小值相同,因为这样碎片问题会非常严重:如果我们每次大对象申请内存时,heap都扩展5% ,譬如50M ,碎片问题很大程度上可以避开,程序性能也高些(寻找可用空隙和分配耗时,以及每次GC时间拉长)。
以上的具体阐述,请参考我在上文推荐的几个URL ,另外再推荐三个宝贵的链接:
http://www-1.ibm.com/support/docview.wss?rs=180&context=SSEQTP&q1=fragmentation&uid=swg21176363&loc=en_US&cs=utf-8&lang=en
http://www-900.ibm.com/cn/support/viewdoc/detail?DocId=2447476A10000 (IBM 技术支持告诉我的,太重要了!)
http://www-900.ibm.com/cn/support/viewdoc/detail?DocId=2847476B08000

我想大家应该会问:我怎么能够肯定我的OOM 问题是heap 碎片造成的呢?下面的方法可以验证。
OOM 发生时,JVM 会产生一个heapdump文件。然后用GarbageCollector 分析出该OOM发生时刻,JVM 去申请的空间,譬如约235k。此时,你再用HeapAnalyzer 去分析此时的heap快照里面的gap size 大小(空隙大小)和各自的可用数目。你会发现,大于235k的空隙个数为0 。这就是碎片导致OOM 的证据。

另外,有人会问:我怀疑我的OOM 是因为程序内存泄漏造成的,怎么去验证?

你可以用HeapAnalyzer 分析发生OOM 时刻的heap快照,工具会罗列出哪些对象怀疑有内存泄漏,譬如Cache 对象都非常大(但你可以确定它不是内存泄漏)。另外,分析这次宕机(从这次虚拟机启动到宕机这段时间)的heap走势,如果曲线明显是向上倾斜,也就是那种典型的内存泄漏图,就有可能是内存泄漏。当然,还必须结合heap快照。
内存持续上升在JVM 开始一段时间很正常,因为JVM 对第一次访问到的Class 对象,譬如一个典型的Web 应用,就有jdk的classSpring 或Hibernate的class 对象,它们都会被缓存下来(ClassLoader原理) ,一般均不会被GC。当大多数class 对象缓存差不多(当然还可能有一些Singleton对象,不过不怎么占分量),JVM 的Heap就平稳了,呈一水平波浪或锯齿线。
如果可以用JProfiler 这类工具实时监控,就更容易确诊了。

经过一番周折,我们终于看到了一线希望了

在一定的准备后,我们决定对WAS 进行性能调优了。WAS 的调优参数,可以分为两个部分:JVM级别和WAS 级别:
JVM
:主要是GCHeap
WAS:Thread PoolJDBC DataSource
当然要调节,你需要明白你的目标是什么,调节依据是什么,怎么计算,绝对不是凭空想象的,譬如heap 最小值1024M ,日志证明,该参数非常不适合我们的环境。具体细节,留给后文吧。

战战兢兢地,中午12:00 ,我们给产品环境下的WAS 调节参数、重启,同时优化了AIX的IO 相关参数。

我试着设置了一下JVMk-clusterp-cluster 。下午15:00左右,WAS 挂了,AIX 也挂了。这下麻烦可大了。我们都慌了,马山客户的老总就来电话了,一阵哗哗啦啦。实在无奈,让客户那边工作人员通知机房(服务器托管处)工作人员重启AIX。我也不得不强行更改刚才的参数,立即设为另外一个值。
其实,我把那个两个cluster 值确实设置太大了,我把它们设置为推荐值的5倍,譬如p-cluster65k×110%×5 。另外一个愚蠢的设置就是把最小heap设置为2048M (AIX有4G 内存)。
后来我恢复到约正常的值,也就是去掉那个cluster5 ,另外分配了一个30%的大对象区(如果1000Mheap ,就是700M300M )。

就这样,系统持续正常运行了三天,以前可是一天一down 。当在三天后再次宕机时,我们都没有自信了 。不得不通过AIXcron ,继续每天深夜11点的WAS 定时重启。
不过,那次宕机,包括以后的几次宕机,再也没有出现OOM 错误了 ,但系统依然不稳定。虽然我可以说OOM问题解决了,但领导和客户需要的并不是这种结果。

其实,在这个时候,我们已经发现我们系统的四大问题:
1
WASJVM 参数:OOM问题
2
AIXIO 和Paging Spacing不足:AIX 日志后来显示错误
3、AIXWAS 分区空间不够:WAS的日志膨胀一周就把那个opt 分区塞满了。
4、应用程序的JDBC 连接池:我们20 来个应用,一个20 connections,DB2 数据库有时被撑死。

也就是说,我们最初在客户那儿部署时,用的默认值根本不行。而且,部署涉及多人,人员之间出现断层。如果我们只是按OOM ,无疑是走入死胡同,必须全局考虑!
但是,项目组实力薄弱,公司范围内就没有对AIX 精通的。不过项目组原来有一个搞银行系统,在AIX下开发,就他熟悉些。我当时对AIX 也比较陌生,你们从Linux转到AIX ,你就知道它有多别扭了。命令都自定一套(也许因为是Unix元老吧),那个shell 也超级别扭,而且参考书特少。不是自诩,我两年前负责一个高负载的Linux服务器管理一年多,也是玩得很转的。

就这样,他负责AIX 的相关问题,我负责WAS 相关的。
但是,现实环境,已经不允许我们再试验下去了 。我们必须找到一条绝对可靠的对策!
这就是下文的CMS 系统大迁移,服务器再次优化。

五、隔离CMS 系统,服务器优化
从前面的介绍,大家应该记得,我们开始是固定CMS ,分离其它应用,但遭遇失败。现在是反过来,干脆把CMS 系统赶出WAS 平台

说实话,项目经理做这个决定,我认为已经是鼓出很大勇气了

当时我们想在一个备用AIX 机上安装CMS 产品测试,但最后还是没有做成:
CMS
这类文章发布系统很难安装,也不好测试,又没有liscence ,而且还有一堆准备工作。绝对没有著名的openCMS安装那么简单,当然功能远远比它复杂。而且,我们当时也低估了后来的工作,总觉得问题好解决。

在很遥远的06 年中期,CMS 厂商在客户那边一台AIXTomcat 上部署了一套CMS产品。但当时客户执意要求将其跑在WAS 上,也就是现在的情况。最开始,客户还要求我们必须用WAS的集群( 我们买的就是WAS 的ND版) ,无奈该CMS 不支持。要是集群,又是死伤一遍。其实,现在想想,我们当时太被动,CMS这种东西,就供公司的几十个编辑用,一个普通Tomcat 就完全够用。而且,把它和面向公网的Internet应用混在一起,完全没有必要。也许,被动是因为我的实力造成的。

我们决定背水一战时,已经做过周密的计划:某年某月某日晚上8:00......
CMS
产品负责人现场切换
xx
(我)负责WAS 相关参数调整
yy
负责AIX 参数。
zz
负责应用的测试
…..

总之,该行动涉及到客户方、产品提供商、公司高层、项目组。每个人都密切关注,不下20 人。每个人都守在电脑前,随时听候调遣,当天晚上,我们都没有准备回家睡觉,大家齐心协力。

真没想到,整个式切换工作,一个小时就顺利完成 !第二天,客户编辑打开浏览器,她们一定想不到昨晚大家准备经历一场厮杀….

系统持续平稳地运行了一周,然后是漫长的五一,我回湖北黄冈老家休息了八天。回来时,一切依旧。

  相关解决方案