- 浏览: 75455 次
- 性别:
- 来自: 厦门
文章分类
最新评论
-
覃永波:
引用[*][img][/img][url][/url]
java 读取txt,java读取大文件 -
bits00:
...
tcpdump网摘
转自 http://chain.blog.163.com/blog/static/14084852320117164610479/
近段时间,我们项目中用到的WebSphere应用服务器(WAS),但在客户的production环境下极不稳定,经常宕机。给客户造成非常不好的影响,同时,也给项目组很大压力。为此,我们花了近一个月时间对其诊断,现在基本上稳定了,需要继续观察一段时间。现在我主要将工作做一个阶段性的总结。
我们的产品环境是:WAS6.0+DB2 8.1+AIX5.3+RS/6000。在该产品环境下,出现的问题非常多,现象如下:
1.WAS经常不稳定、宕机几乎一天一次,经常报告OutOfMemory(内存泄漏吗?NO)。
2.DB2连接数过大,有时把DB2撑死,有时也把AIX撑死。
3.AIX虚拟内存报错、分页报错、IO也报错、还有很多其它莫名奇妙的错。
总是,每次问题发生的现象和理论上的总是不一致,导致我们不知道从何入手,也无从检测自己的优化参数。咨询过多次IBM技术支持,只解决了某些局部问题。
虽然问题依然存在,但我想,解决问题的思路、特别是理论基础,还是有一些规律和原则。
对于WAS这块,我近段时间的主要时间集中在以下几个方面(时间顺序):
1、Java性能监测工具:Jprofiler,也用到Jprobe。后来发现Jprofiler在AIX下几乎不可用。
2、IBM Java虚拟机和WAS技术细节,特别是IBM JVM的GC原理,我发现它和sun、bea的差别很大。
3、IBM的heap分析器Heap Analyzer、GCCollector。这两个事后监测工具非常实用,特别是我们的产品运行环境,非测试环境。
4、某些Application的怀疑和诊断。
5、AIX诊断,我几乎没有这个能力,只能常规监测一下,需另请高人。
我打算将本文分成以下几个部分总结:
1.JVM原理、IBM JVM的GC策略和调优。
2.Jprofiler和IBM工具的实际体会
3.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技术和诊断讲解很深入。
我不得不提的是,在查资料这块,BEA和Sun都有很好的官方文档和论坛支持,并且官方文档导航非常好。虽然IBM的诊断资料也不少,但需要搜索,其搜索是很痛苦的。而且,IBM官方论坛很差。如果用IBM的产品出问题,切记:找IBM技术支持,千万不要蒙着头搞!反正它们的产品很少免费。说实话,它们的技术支持还是挺负责的,一般会为你推荐很多support资料,而该资料往往都在developerworks网站上,属于support那个频道,但你就是搜不着。
Java虚拟机规范概要
研究Java虚拟机,首先要了解Sun的Java虚拟机规范。现在,该实现版本很多,如比较有名的Sun、IBM、BEA、Apple、HP、MS、Apache Harmony。它们都实现了JVM规范,但有各自扩展。譬如,针对IBM虚拟机的堆碎片导致OutOfMemory(OOM),在Sun的虚拟机上就不会发生。Sun的JVM有maxPermSize的概念,IBM就没有,如果你设置这个参数,虚拟机根本就启动不了。
比较有意思的是,学Java,就一定要了解各种规范,这和MS的风格很不一样。Sun总是在定义一些规范,实现都留给各厂商。我们除了理解规范本身外,一定要理解规范和实现之间的关系,譬如JDBC规范和JDBC驱动的关系,它们是怎么组合到一起的。要是你用过php的xml解析库,或db函数,就会体会深刻,它们可没有什么规范可言,所以每个数据库厂商的db函数用法都不一样。我推荐大家研读一下HSQLDB的jdbc和Tomcat的servlet相关实现,因为我认为它们还是比较好懂的。
JVM规范只是定义一个虚拟机该做什么,但它并没有要求你该怎么做。例如我们最常见的Servlet规范,在该规范中,有HttpServletRequest、HttpServletResponse,HttpSession等接口,但它们的实现都留给了各个容器厂商。遗憾的是,规范留下的空白,会把我们这些开发人员给整惨了:容器间移植有时候就是恶梦。譬如J2EE并没有SSO规范,但它很重要,我以前专门针对它做过WebSphere AppServer和Weblogic AppServer的SSO项目,差别还是不小,不过还是有点共通,那就是都遵循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:Bootstrap、Extention、Application,该三类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可能并没有被加载。这就是ClassLoader的delegate现象。常见的有log4j、common-log、dbcp会出现问题,因为它们很容易被人塞到这个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,它最终由Bootstrap ClassLoader加载。并且,每个ClassLoader只有一个父ClassLoader。Class查找的位置和顺序依次是:Cache、parent、self。
Hierarchy:上面的delegation已经暗示了一种分级结构,同时它也说明:一个ClassLoader只能看到被它自己加载的classes,或是看到其父(parent) ClassLoader或祖先(ancestor) ClassLoader加载的Classes。 在一个单虚拟机环境下,标识一个类有两个因素:class的全路径名、该类的ClassLoader。
我碰到的一个典型的例子是:在做WAS的SSO开发时,由于我们的类是由WAS在启动时加载,该ClassLoader比下面的部署的Applicaton的ClassLoader的级别高。所以,在我们自己的类中没法用到应用程序的连接池,必须自建。
代理模型是Java安全模型的保证。譬如,我们自己写一个String.java,并且编译、package到自己的java.lang包下。按照代理模型,当前线程的ClassLoader会将其代理到父ClassLoader,父ClassLoader(最终会是Bootstrap)会找到rt.jar下的String.class,也就是说我们的String.class不会捣乱。
自定义ClassLoader
我们前面说过,自定义ClassLoader的缺省父ClassLoader是Application ClassLoader。一般的应用开发用不到它,但我们最好理解。因为在内存泄漏查找、应用程序部署出问题时,很多都和它有关。
譬如,内存泄漏是怎么产生的?这就涉及到ClassLoader和Class的生命周期。我曾经碰到这样一个问题:我们的程序用到了Webwork和Spring框架,当部署到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、server、share三个目录吧(ClassLoader顺序从左到有),另外也有用户应用的WEB-INF目录,它是我们自己开发的。
3、允许Classes卸载
如果没有自定义的ClassLoader,那么我们自己应用中的classes永远都不能被卸载,因为这些类被Application ClassLoader加载后cache起来了,我们的classes一直对该ClassLoader有引用,而该系统级的ClassLoader永远都不会被卸载,除非JVM shutdown了。JSP和Servlet的动态部署就用到这个特性。
先说说我接手这项工作的经历吧:该项目大部分是06年10月就部署在客户那边了,到07年3月份,WAS宕机问题实在无法忍受,我才加入进来,前半年有另外一位同事断断续续处理,但对问题一直都无可奈何,而且项目负责人也没有引起足够的重视。可想而知,最后付出的代价是非常惨重的。在这近半年的时间内,服务器宕机63次。每次宕机时,WAS的JVM会dump出一个heapdump.phd文件(heap快照),然后JVM就死掉了,当然,此时WAS也停止了响应。一般我们的做法是重启,最后是干脆AIX每天晚上定时重启。有时候一天还死多次。这是我接手后,用IBM的分析工具得到的截图。对截图的分析,留给后面对应的部分吧。
服务器不稳定、宕机问题,拖延到最后,客户愤怒了,公司高层也害怕了,部门还专门成立了八人攻关组。当然了,我当时的压力也非常大,因为我是技术负责人,也就是实实在在干活、想主意的。
服务器诊断那段时间,从前到后,我们也是沿着一条线走下来,虽然最后发现很多路都走不通。现在就按这个思路,也就是时间先后一步步叙述吧。我想,大家如果也碰到类似应用服务器诊断,应该思路差不多。
术语说明:
IBM Websphere Application Server:WAS,WebSphere本身是一个平台,产品家族
OutOfMemoryError:OOM,内存泄漏,内存溢出
Gabage Collection:GC,自动垃圾回收
Content Management System:CMS,就是给新闻类门户网站编辑们用的系统
我们诊断大体上经历了以下几个阶段:
1、按Job调度线程池引起内存泄漏诊断:因为很多次OOM是发生在某个特定时候,譬如14:30、22:40左右。
2、按应用程序引起内存泄漏诊断:用JProfiler等工具探测:因为总是发生OOM。
3、分离WAS怀疑有OOM的应用:因为每个WAS应用太多,20来个,混一起没法定位。
4、用IBM官方heap、GC分析工具。以及和IBM技术支持联系。WAS、AIX参数优化。
5、隔离出WAS超级恶魔程序:一个CMS产品。
6、WAS、AIX参数优化、设置。
我们走到第5步时,才出现效果。计算一下,那时已经过去一个月了。服务器宕机、系统不稳定,在这个验收的时候,客户已经忍无可忍,以致后来的每一次行动都得胆战心惊得去做。
一、按Job调度线程池导致内存泄漏诊断
因为从我们WAS的日志(默认是native_stderr.log)来看,最近半年的宕机时间都有一个明显时间规律。
我想,做过Java服务器性能调优的朋友,都知道在Web容器里面启线程池是个不太好的做法,因为Web容器本身有一个线程池,譬如Servlet线程池(Tomcat默认起25个),而自启的线程池很容易导致Servlet线程管理混乱,最终导致GC问题。我们的现象似乎和那很符合。如果我们沿着这个思路做下去,具体怎样实施呢?
我们的WAS上部署了20个左右的Web应用,譬如Lucene全文检索、B2B行业数据同步等,都是通过Quartz的Job调度做的,当然还有很多其它调度。当时,由我负责,通知相关负责人,将定时调度暂时去掉。观察了几天,后来发现问题依然存在,不过时间有点随机了。
不过,最后还是发现OOM不是由Job调度引起的。也就是说,我们这个方案是失败的。而且,我们的很多想法都是臆测的,没有可靠的根据,也没有方向,再加上我是第一次处理这种问题,这导致后来查找问题的艰难。但是,仔细想想,我们又能拿什么做依据呢?出现OOM错误,我想大多数人想到的,除了JVM参数设置,就是内存泄漏。其实,OOM发生有很多种情况,在IBM、Sun、BEA等不同虚拟机上,因为GC机制不一样,所以原因一般都不同,容易定位难度也不一样。下文会谈到。
于是,我们干脆釜底抽薪分析问题吧:用JProfiler检测。
二、按应用程序导致内存泄漏诊断,JProfiler检测
如果遇到OOM问题,我想大家都会想到内存检测工具,现在最可靠的还是下面三种分析工具:Borland 的Optimizeit Suite,Quest的JProbe,ej-technologies的JProfiler。但面临三个问题:
1、三个都是商业产品,公司暂时没有买,必须自己下载,而且要找序列号。
2、工具必须支持AIX5.3+JDK1.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)。
下面是Sun官方文档:
JDK1.42及以前是JVM PI:http://java.sun.com/j2se/1.4.2/docs/guide/jvmpi/jvmpi.html
JDK1.5是JVM TI:http://java.sun.com/j2se/1.5.0/docs/guide/jvmti/jvmti.html
具体到JProfiler的配置上,专门针对JDK1.4和1.5的JVM配置差别很大。
我用的JProfiler是4.31版本,透露给大家一个万能序列号吧(这东西不太好找),对各版本应该都支持。深入了解Java,这类工具是不可少的:
Name: License for You
Lincese Code: A-G667#42616F-10kg1r134fq9m#2217
为了保证真实环境的检测成功,我做了大量的试验,譬如:
1、Windows系列的本地、远程测试。
2、AIX的远程测试。
3、Tomcat5.0、WebLogic8.14、WebSphere6.02,以及上述两种方式的组合测试,排列组合,场景不下10个。
当时也参阅了大量JVM文档,JProfiler官方几百页英文文档,辅助的JProbe对照。而且也制造过内存泄漏造成的OOM场景。当然,要是在几个月前,在客户那边部署的测试环境时,就进行测试该多好啊。
在公司内部,我用JProfiler测试了我们当时部署的几个应用,没有发现内存泄漏,所以,我们最怀疑的是就是CMS系统。因为出问题的那个WAS上它占去了90%的负荷(我们有多台AIX、WAS服务器)。该CMS超级庞大,感觉著名的赛迪网就用它,当时该CMS厂商给我们部署都花了快一个月。所以再重新部署一套测试环境也挺困难。另外,CMS提供商不给lisence。现在测试,客户早就对我们恼火了,当然不怎么配合,这对我们工作的开展就有很大的挑战。
在大致可以确定万无一失的情况下,我们最终决定在客户的真实环境下测试。也就是让JProfiler的agent端直接在WAS的JVM里面启动(北京IDC),然后远程(大连)监控。
本来该模式在另外几个应用的测试都通过了(因为北京IDC那边好几台AIX服务器)。但一部署上,客户的一些编辑用CMS时就感觉超级慢,尽管我们用了JProfiler的最小负载模式。半个小时后,客户实在无法忍受,打电话过来,又把我们部长和经理训了一顿,还要写书面报告。我们被迫马山中止测试,恢复现场。
虽然JProfiler也支持客户那边的环境,但还是有bug,至少负载一高就有严重的性能问题,几乎导致系统挂起,这是我当时没有料到的。JProfiler一启动,WAS的启动时间就由原来的3分钟降到10分钟,而且系统响应明显变慢,我们具体的环境如下(排列组合恐怕不下20种):
1、AIX5.3,Power PC64位(不是32位)
2、WebSphere6.0
3、IBM JVM1.42
4、Remote 模式
我后来仔细读了一下JProfiler的changeLog,发现对上面的环境支持不够也很正常,因为官方在最近的4.3.x版本下陆续有对IBM JVM和Websphere6.0的features和bug fix:http://www.ej-technologies.com/download/jprofiler/changelog.html
进行到这一步,我忽然觉得无计可施了 ,此时已经过了三周。上面的策略,我认为是很正统的处理方法。谁怪我们当初项目上线时没有进行充分的测试呢?其实,这类测试没做也正常,OOM这类问题谁都无法预测。
到这个时候,我想肯定有人会问我?你知道导致JVM的OOM有几种情况吗?在当时,我想到了以下五种:
1、JVM的heap最小、最大值没有设,或不合理。
2、JVM的maxPermSize没有设置(这个IBM的JVM没有,一设置JVM就起不来)。
对于Sun和BEA的JVM,以上两种参数设置,可以排除90%以上的非程序内存溢出导致的OOM。
3、程序内存泄漏
4、有的JVM,当在80%的CPU时间内,不能GC出2%的heap时,也发生OOM(IBM的JVM就是,但我没有验证)
5、JVM本身内存泄漏(JVM有bug不是不可能)
现在的难题是,如果是那个可怕的CMS程序本身有内存溢出,在产品环境下,我们怎么去验证?我们自己开发的10来个web应用,测试并不是很难,在公司测试都可以。但是,我现在最想解决的,就是CMS测试的问题。而且,在我们那种环境下,该CMS产品供应商并没有透露成功案例。
其实,最后发现,并不是内存泄漏造成的,因为我们的heap走势都很平稳。纳闷的是,有1000M的heap,每次在heap只被占用400来M时就发生OOM,没有任何预兆。大家猜猜,会是什么原因 ?这个问题我到后面相关章节再说吧。
既然我们所有的矛头都指向那个可怕的CMS系统,现在就是考虑隔离的问题。如果我们验证这个问题是CMS造成的,那么大部分责任就应该由CMS厂商承担。既然CMS我们不敢移(费劲,而且客户在正式用),那我就移我们开发的10来个web系统吧。
三、移出除CMS系统以外的所有应用
说起来容易啊,做呢? 隔离(移动)工作由我负责,具体涉及到10来个相关负责人。转移工作,必须处理好很多问题,就说几个印象最深的吧:
1、某些应用,如Blog和BBS,都涉及到文件、图片上传目录和产品本身的环境,如 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有关的东西....”。我真的很生气,它们的产品都折磨我们半年之久了。不过,看他们对IBM的WAS和JVM也不懂,我也就不想再说什么了。下面是我们的邮件:
我想以前你们也这样做过,现在我们分析错误日志,发现有一个现象,在宕机时,总是找不到文件,我看就是Websphere或是AIX IO资源不够,不知道是什么导致的。但是,我们自己的应用,基本上没有什么IO,除了一次load几个配置文件。不过,我觉得你们WCM的IO操作挺多的,不知道你对日志有什么新的发现。
客观的说,这几个月来,宕机那台服务器,除了你们的XXX,就以论坛和blog为主,而且他们都是开源的。在频繁宕机的06年1月份,我们的论坛和blog还没有上线。现在我们不得不每天晚上11点定时重启,但这也不是长久之计。现在,我们进行分离遇到很大阻力,原来想把你们的XXX单独分离出来,在当前的环境下,不是很现实,如安装、测试(负载、定时服务),所以现在分离我们自己的应用,但当前在产品环境下,客户方阻力也很大。
希望尽快能够得到你们的问题建议和方案。
文中说到了IBM的两个分析工具,这也是我们后来的救星:我们就是需要这种离线分析工具,因为实时检测已经证明不现实。但我始终对该分析出来的结果抱怀疑态度,直到我去深入IBM的JVM以及和IBM的技术支持交流......柳暗花明啊 ,至少看到了一点希望,不过最后我们还是失望而返。
四、用IBM的HeapAnalyzer和GarbageCollector检测
找到这两个工具,已经是够费劲了,因为以前找的IBM HeapRoot工具,让我对这类工具很失望。而且,这两个工具,只有在IBM的Techinical Support网站能够搜索到,但很不容易,因为那两个工具,并不是象IBM的Websphere产品那样宣传,它只在IBM Techinical Support文章的某些角落里出现。要知道,Techinical Support是IBM很重要的收入来源,这类文档,他们并不会让你很轻易就拿到,比起BEA WLS的支持网站dev2dev差远了。
具体诊断细节我就不详述了。我认为,IBM的WAS或JVM出了性能和OOM问题,这两个工具是最有效的,而且是离线分析工具,比起那些实时Profiler工具,某些场合有绝对的优势:譬如我们目前的产品环境,你只能分析宕机后的日志,实时分析前面已经验证是不可行的。
从日志分析,我们最终得出结论,我们购买的CMS系统有严重的碎片(大对象)问题,而该问题是OOM的罪魁祸首,而且IBM工程师也得出了同一结论。不过,在起先我们得出这一结论一周后,我还始终不相信heap碎片会导致OOM,直到IBM工程师总是向我强调。
我想很多人也是不太相信,因为大多数人用的都是Sun的JVM,譬如Windows、Solaris上的hotspot。而且,Sun JVM出问题,如果是配置的问题,一般通过配置heap最大最小值,以及maxPermSize都可以解决。Heap碎片导致的OOM,只有BEA的JRockit和IBM JVM上发生,不过JRockit有专门文档说明,而且很容易找到(就在jdk的文档里面)。
配置heap最小最大值,我想大多数人都有经验。对于Sun的JVM来说,一般可以设置heap最大最小值一致,也是推荐的做法。因为它的GC策略默认是复制、分代算法。也就是说,它会将heap分成不同的几个区,譬如Solaris JVM中最上面有两个大小相等的区。GC时刻,将一个区的存活对象复制到另外一个对等区,垃圾对象就算遗弃了。这样在heap里面,就不存在碎片问题。另外,根据Java对象的存活期不同,将之转移到不同的区(Tenured区),存活最长的在最底部(火车算法),这也就是分代模型。具体请参考官方文档:http://java.sun.com/docs/hotspot/gc1.4.2/
对于maxPermSize(Permanent Generation),主要和那些加载到JVM里面的Java Class对象相关,它的空间不是在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提示。
但是,对于IBM的JVM,情况就完全不一样。它的默认GC策略并没有采取复制、分代。这个可以从GC日志分析出来。它不像Sun的JVM那样,有个单独的方法区,它的方法区就放在Java Heap里面。JVM规范里面并没有要求方法区的必须存放的位置,因为它只是一个JVM实现问题。
在IBM的JVM里面,这些对象一般分配在称为k-cluster和p-cluster里(cluster又是属于Heap),而后者一般是临时在heap里面申请。并且,这些cluster是不能GC,或是被移动重排的(Compact过程)。这就导致Java Heap里面就如同马蜂窝,但不同的蜂孔又不能合并,于是,当我们程序里面产生一个大对象,譬如2M的数组(数组必须分配在连续的内存区)时,就没有可分配空间了,于是就报告OOM。这些不能被移动的cluster就称为所谓的碎片。此时,JVM的Heap利用率可能不到50%。
当然,通过一定时期的GC日志,可以计算出cluster的合理大小(专门在Java Heap的底部),另外,还可以为这些大对象专门分配大对象区的(超过64k的对象)。
通过上面的理论介绍,我想大家一定知道了为什么IBM的JVM里面不推荐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的class、Spring或Hibernate的class对象,它们都会被缓存下来(ClassLoader原理),一般均不会被GC。当大多数class对象缓存差不多(当然还可能有一些Singleton对象,不过不怎么占分量),JVM的Heap就平稳了,呈一水平波浪或锯齿线。
如果可以用JProfiler这类工具实时监控,就更容易确诊了。
经过一番周折,我们终于看到了一线希望了。
在一定的准备后,我们决定对WAS进行性能调优了。WAS的调优参数,可以分为两个部分:JVM级别和WAS级别:
JVM:主要是GC和Heap。
WAS:Thread Pool,JDBC DataSource。
当然要调节,你需要明白你的目标是什么,调节依据是什么,怎么计算,绝对不是凭空想象的,譬如heap最小值1024M,日志证明,该参数非常不适合我们的环境。具体细节,留给后文吧。
战战兢兢地,中午12:00,我们给产品环境下的WAS调节参数、重启,同时优化了AIX的IO相关参数。我试着设置了一下JVM的k-cluster和p-cluster。下午15:00左右,WAS挂了,AIX也挂了。这下麻烦可大了。我们都慌了,马山客户的老总就来电话了,一阵哗哗啦啦。实在无奈,让客户那边工作人员通知机房(服务器托管处)工作人员重启AIX。我也不得不强行更改刚才的参数,立即设为另外一个值。
其实,我把那个两个cluster值确实设置太大了,我把它们设置为推荐值的5倍,譬如p-cluster是65k×110%×5。另外一个愚蠢的设置就是把最小heap设置为2048M(AIX有4G内存)。后来我恢复到约正常的值,也就是去掉那个cluster的5,另外分配了一个30%的大对象区(如果1000M的heap,就是700M+300M)。就这样,系统持续正常运行了三天,以前可是一天一down。当在三天后再次宕机时,我们都没有自信了。不得不通过AIX的cron,继续每天深夜11点的WAS定时重启。不过,那次宕机,包括以后的几次宕机,再也没有出现OOM错误了,但系统依然不稳定。虽然我可以说OOM问题解决了,但领导和客户需要的并不是这种结果。
其实,在这个时候,我们已经发现我们系统的四大问题:
1、WAS和JVM参数:OOM问题
2、AIX的IO和Paging Spacing不足:AIX日志后来显示错误
3、AIX的WAS分区空间不够: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厂商在客户那边一台AIX的Tomcat上部署了一套CMS产品。但当时客户执意要求将其跑在WAS上,也就是现在的情况。最开始,客户还要求我们必须用WAS的集群(我们买的就是WAS的ND版),无奈该CMS不支持。要是集群,又是死伤一遍。其实,现在想想,我们当时太被动,CMS这种东西,就供公司的几十个编辑用,一个普通Tomcat就完全够用。而且,把它和面向公网的Internet应用混在一起,完全没有必要。也许,被动是因为我的实力造成的。
我们决定背水一战时,已经做过周密的计划:某年某月某日晚上8:00......
CMS产品负责人现场切换
xx(我)负责WAS相关参数调整
yy负责AIX参数。
zz负责应用的测试
…..
总之,该行动涉及到客户方、产品提供商、公司高层、项目组。每个人都密切关注,不下20人。每个人都守在电脑前,随时听候调遣,当天晚上,我们都没有准备回家睡觉,大家齐心协力。真没想到,整个式切换工作,一个小时就顺利完成!第二天,客户编辑打开浏览器,她们一定想不到昨晚大家准备经历一场厮杀….
系统持续平稳地运行了一周,然后是漫长的五一,我回湖北黄冈老家休息了八天。回来时,一切依旧。当天晚上,我们这边主要做了两项工作:
1、JVM的Heap参数,共五个。
2、AIX的IO、Paging Space等共六个。
当然还有其他人的工作,譬如测试、监控。
还有一个非常重要的方面:JDBC连接池。我们原来是在每个Web应用里面独立设置,这样20来个应用就有几百个DB连接,一不小心DB就给撑死。现在统一交由WAS内置DataSource处理,总共连接不到30个。其实,我们项目开始部署时,就是这样做的,但当时WAS内置的DataSource对JTA(XA)支持有bug (这个和IBM技术支持确认过,但他们没有给予很好的解决方案),不过Datasource还是配好的。
但是这个工作已经属于WAS性能优化的主题了,而且优化值必须持续观察一段时间,通过专门的分析工具来计算。优化本身,是一项很考验人的工作,我就简单说一下最实用方法吧,也许是专门针对IBM的产品。
1、清理归零WAS日志。然后启动WAS,生成日志(-verbose:gc默认是开的)
2、让WAS持续运行约两周,让JVM Heap占用曲线平稳一段时间即可(用IBM的Garbage Collector分析观察)。
3、在AIX的shell里,产生heapdump.phd文件,也就是heap快照。命令:kill -3 pid (pid是WAS的PID,通过ps –ef查看),观察heap当时的碎片情况,是否需要单独分大对象区(一般不需要设置),特别是那个方法区Class对象大小(p-cluster参数)。
4、通过GC工具,观察GC平均时间、Heap实际占用情况。Note: GC是一个Stop The World过程,也就是说GC时系统对外不响应,多CPU也不例外。看你的应用实际需求了,GC持续时间和频率是矛盾的,另外还有性能考虑。一般Web应用,我想让GC持续时间(Pause time)调节到合理值就ok了,譬如0.2到0.4s。
5、根据3可以算出k-cluster值,它是工具推荐值的110%。
6、Heap的最小值是程序刚启动不久的占用值,譬如320M。切记:IBM JVM初始值太大非常不好。
7、Heap的最大值是系统平稳后的100/70。也就是说如果最大值是1000M,那么应该平稳时是700M,还有30%的空余。IBM的JVM默认情下的碎片问题,WAS控制台下操作Heap猛增这种bug,你不得不堤防。Heap最大值不设,AIX下的WAS肯定OOM。
当然啦,我没有考虑到大对象区的计算(虽然我们的应用设置了专门的大对象区),包括IBM JVM支持的分代GC、并行GC,Heap每次expand百分比等。那些情况我们一般不常用,譬如,你的AIX平台一般不是16CPU吧?
一口气写到现在,我忽然觉得该收尾了。下面就说说我对这类工作的整体看法吧。
1、尽量在项目测试和试运行的时候就进行压力、性能测试,当正式投入使用后,如果发现类似问题,代价非常大。躲过算你运气好,一般来说,可能你们系统没多少人用,也不是核心业务系统,譬如一般的电子政务。
2、千万不要低估了技术风险,用IBM的系列产品尤其要慎重,出问题一定不要忘了技术支持。而且,查资料时,建议用google English,因为象WebSphere这类问题,很少有中文资料。
3、程序部署环境建立时,就要考虑到日后的正式环境,譬如AIX的Paging Space、IO、分区大小,默认值往往是不行的,而且在产品环境下改这些值,往往非常难。
4、在项目开发初期,就考虑到日志的问题,因为它分散到每个方法内,必须慎重定义好debug、info、warn、error级别,不要随便忽视异常(catch里面不记录),到真正程序出问题时,它就是我们的最重要的依据之一。当然这主要是功能性问题诊断。另外对于高负载网站,日志文件往往非常大,各级别日志千万不要混在一起,否则找问题就很困难了。
5、怎么说呢,别死扣技术,以为什么都可以通过技术解决。你看我们最大的问题就是把CMS给移到Tomcat下。你要是问我,为什么CMS产品会导致系统这么多的问题,我也不知道,到那时候,我确实也不想深入。我只要知道,赶出你这个应用,我的系统就好控制多了。而且,那个CMS系统,在Tomcat下,就是跑得服服帖帖的,非常稳定。难度是可恶的WAS?不过那CMS,据IBM工程师,包括我们二次开发,都觉得够烂了,每个jsp页面都打开、关闭DB connection(7年前的jsp开发模式),还有那么严重的大对象问题。
发表评论
-
飞鸽收藏(转)
2012-04-27 18:18 1367我们常在局域网内用飞鸽进行沟通、文件传输。有天突发奇想,要是我 ... -
主题:边读边写【7】 ----java 多线程实战【Thread /Executors】(转)
2012-04-23 11:39 1389http://www.iteye.com/topic/1122 ... -
牢骚一下,JavaFx 2.0太坑人了
2012-04-18 20:45 17用了半周看了下JavaFx2.0 ,看官方例子感觉还不 ... -
Java中一些关于日期、日期格式、日期的解析和日期的计算 (转)
2012-04-12 11:00 0http://www.blogjava.net/Jkallen ... -
操作property文件(转)
2012-04-12 10:51 965对于一些常量我们经 ... -
Java 系统运行时性能和可用性监控
2012-04-12 10:40 1182简介: 当今的许 ... -
Jconsole_一个监控JVM资源的JDK自带小工具 (转)
2012-04-12 10:32 1223最近一直忙着测一个tomcat的接口,测试中LR总出现2779 ... -
探索JVM运行状态的利器—JVMPI,HPROF_剑_百度空间 (引)
2012-04-12 10:29 1387.什么是JVMPI?Java Virtual ... -
java 格式化小数
2012-03-09 16:53 1522DecimalFormat 是 NumberForm ... -
org.apache.commons.net.ftp.FTPClient 例子(转)
2012-03-02 09:26 1463转自 http://zhangnet1.iteye.com/b ... -
java查询一次性查询几十万,几百万数据解决办法(转)
2012-02-29 09:15 1002java查询一次性查询几十万,几百万数据解决办法 很早 ... -
java 读取txt,java读取大文件
2012-02-26 19:39 2117java 读取txt,java读取大文件 设置缓存大小 ... -
StringUtils用法+StringUtils详细介绍
2012-02-26 19:38 933StringUtils用法+StringUtils详细介绍博文 ... -
java 字节流网址收藏(暂)
2012-02-07 10:43 749Java I/O关于缓冲区部分提高性能的源码分 ... -
String类substring方法导致的Java内存泄漏问题 (转)
2012-01-06 10:10 1447此问题在项目中被发现,经查看JDK源码(JDK1.6), ... -
Swing 线程之SwingUtilities.invokeLater()(转)
2012-01-06 10:00 1224声明:本文章转自 http://blog.csdn.ne ... -
Swing 线程之SwingUtilities.invokeLater() (转)
2012-01-06 09:59 2声明:本文章转自 http://blog.csdn.net/b ... -
Java Swing GUI多线程之SwingUtilities.invokeLater和invokeAndWait
2012-01-05 16:55 5438在Java中Swing是线程不安全的,是单线程的设计, ... -
用java.util.Timer定时执行任务
2012-01-05 16:11 844如 ... -
ubuntu java配置
2011-12-30 20:54 1082Ubuntu10.10安装sun-Java6-jdk ...
相关推荐
Java虚拟机工具接口(JVMTI,Java Virtual Machine Tool Interface)是Java开发工具包(Java SDK)的一个核心组件,自Java 2 SDK Standard Edition 1.5.0版本起引入,用于替代实验性的JVMPI(Java Virtual Machine ...
HPROF Agent是Java Development Kit (JDK)中附带的一种JVM TI Agent,它用于演示JVM TI的功能,并作为过去版本中基于JVMPI的旧版HPROF的替代品。随着JVM TI的引入,它取代了JVMDI和JVMPI,提供了更先进的性能监控和...
在Java SE 6中,JVMTI取代了之前的JVMPI和JVMDI,成为统一的接口标准。 使用Instrumentation时,开发者需要编写一个Agent类,该类需要实现`premain`或`agentmain`方法,这两个方法会在JVM启动或Agent加载时被调用。...
JVMPI 是 Sun Microsystems 为 JVM 设计的一个接口,允许开发者插入自定义的探查器(profiler),以收集关于应用程序运行时行为的数据,如方法调用频率、内存使用情况、线程活动等。这个特性对于性能优化、问题诊断...
【标题】"SimpleProfiler-开源"是一个专注于Java内存分析的工具,它利用了JVM(Java虚拟机)的JVMPI(Java虚拟机接口)来实现。这个工具的设计目标是提供一个轻量级且高效的解决方案,帮助开发者检测和解决与Java...
JVMTI(Java Virtual Machine Tool Interface)是Java 2 SDK Standard Edition版本1.5.0中引入的一个重要工具接口,它作为JVMPI(Java Virtual Machine Profiling Interface)的升级版,提供了更为强大和灵活的性能...
JVM Profiler Interface (JVMPI)是大多数分析器的基础,允许开发者创建工具监测JVM关键事件。JProfiler利用这一接口,收集、过滤和分析大量运行数据,以控制数据流并定制监控范围。 4. **JProfiler简介** ...
1. **原理简介**:Rational PurifyPlus通过Java虚拟机假脱机接口(JVMPI)来收集Java应用程序的信息。由于JVM与PurifyPlus作为两个独立的进程运行,因此Java进程需要加载一个名为`PureJVMPI`的共享库来监听JVM事件并...
与依赖JVMPI和本地接口的Profiler不同,Jwebap力求成为一个纯Java工具,不受JVM或OS限制,便于在测试和生产环境间无缝部署。 ### 4. 简易部署 Jwebap的部署应当非常简单,利用动态类加载和字节码注入技术,只需少量...
JProfiler基于Java虚拟机分析器接口(JVMPI)工作,这一Sun Microsystems提供的API允许工具开发商与遵循JVMPI的JVM进行交互,监测关键事件,无论是单一应用程序还是Applet、Servlet或EJB组件。在分析过程中,...
JVMTI是一组由Java虚拟机提供的本地编程接口,旨在支持与JVM相关的工具开发。自Java SE 5起引入并取代了先前的JVMPI和JVMDI。在Java SE 6中,JVMPI和JVMDI已经被废弃。 JVMTI提供了一套代理机制,允许第三方工具以...
xdProf的工作机制基于C/C++实现的Java虚拟机分析接口(JVMPI),这是一个由Sun Microsystems为JVM提供的标准接口。JVMPI允许开发者在JVM执行过程中插入探针,以便在特定时刻捕获堆栈信息。xdProf利用这个接口,周期...
对于需要更高精度计时的场景,可以考虑使用Java虚拟机提供的JVM Profiler Interface (JVMPI)。通过该接口,开发者可以访问更多关于JVM运行时的信息,包括线程状态、内存使用情况等,从而更准确地分析代码的执行效率...
JProfiler基于JVM Profiler Interface (JVMPI) API,允许工具与Java虚拟机交互,监控关键事件,如CPU使用、线程状态和内存管理。分析过程中,JProfiler会控制数据流,提供过滤机制和灵活的正则表达式模式,以便有效...
不同JVM的输出格式可能不同,IBM提供了详细的资源来解析和解读这些日志。 监控垃圾回收可以帮助识别过度使用的对象。过度使用对象可能导致频繁的垃圾回收,从而成为性能瓶颈。使用工具如Tivoli Performance Viewer...
大多数Java剖析工具,包括JProfiler,都是基于Java虚拟机分析器接口(JVMPI)构建的。这个API允许开发者创建工具连接到JVM,监控关键事件,如代码执行和内存使用。分析器需要高效地处理大量数据,通过过滤和模式...
JProbe Suite 通过 JVMPI(Java Virtual Machine Profiling Interface)接口监测 JVM 内部运作状态,适用于 Java 标准应用以及运行于 Java 2 平台企业版(J2EE)上的 Web 应用、EJB 和框架等。其监控设置涵盖了 J2SE...
2. **无本地代码**:大多数性能分析工具依赖于JVMPI(Java Virtual Machine Profiling Interface),这通常需要使用到本地组件。而JIP是纯Java编写的,通过利用Java 5及更高版本中引入的新特性来实现性能监控功能。...