IBM WebSphere Application Server診斷和調(diào)優(yōu)(二)

作者:佚名  2007-06-25
IT168 技術(shù)文檔】近段時間,我們項目中用到的WebSphere應用服務(wù)器(WAS),但在客戶的production環(huán)境下極不穩(wěn)定,經(jīng)常宕機。給客戶造成非 常不好的影響,同時,也給項目組很大壓力。為此,我們花了近一個月時間對其診斷,現(xiàn)在基本上穩(wěn)定了,需要繼續(xù)觀察一段時間?,F(xiàn)在我主要將工作做一個階段性 的總結(jié)。
我們的產(chǎn)品環(huán)境是:WAS6.0+DB2 8.1+AIX5.3+RS/6000。在該產(chǎn)品環(huán)境下,出現(xiàn)的問題非常多,現(xiàn)象如下:
WAS經(jīng)常不穩(wěn)定、宕機幾乎一天一次,經(jīng)常報告OutOfMemory(內(nèi)存泄漏嗎?NO)。
DB2連接數(shù)過大,有時把DB2撐死,有時也把AIX撐死。
AIX虛擬內(nèi)存報錯、分頁報錯、IO也報錯、還有很多其它莫名奇妙的錯。

    總是,每次問題發(fā)生的現(xiàn)象和理論上的總是不一致,導致我們不知道從何入手,也無從檢測自己的優(yōu)化參數(shù)。咨詢過多次IBM技術(shù)支持,只解決了某些局部問題。
雖然問題依然存在,但我想,解決問題的思路、特別是理論基礎(chǔ),還是有一些規(guī)律和原則。

對于WAS這塊,我近段時間的主要時間集中在以下幾個方面(時間順序):
1、Java性能監(jiān)測工具:Jprofiler,也用到Jprobe。后來發(fā)現(xiàn)Jprofiler在AIX下幾乎不可用。
2、IBM Java虛擬機和WAS技術(shù)細節(jié),特別是IBM JVM的GC原理,我發(fā)現(xiàn)它和sun、bea的差別很大。
3、IBM的heap分析器Heap Analyzer、GCCollector。這兩個事后監(jiān)測工具非常實用,特別是我們的產(chǎn)品運行環(huán)境,非測試環(huán)境。
4、某些Application的懷疑和診斷。
5、AIX診斷,我?guī)缀鯖]有這個能力,只能常規(guī)監(jiān)測一下,需另請高人。

我打算將本文分成以下幾個部分總結(jié):
JVM原理、IBM JVM的GC策略和調(diào)優(yōu)。
Jprofiler和IBM工具的實際體會
WAS的診斷體會和AIX調(diào)優(yōu)

    下面開始主題吧,可能比較零碎,另外,開始的理論篇基本上看書都可以,我只是總結(jié)一下,再添加一些自己的理解。

以下是我參考的最重要的兩本電子書和一些網(wǎng)站:
《Inside Java Vrtual Machine》:半部分有約四章我認為非常棒,其它章節(jié)可能意義不大。
《The Java Virtual Machine Specification, 2nd》:前半部分有兩三章很不錯,不過可以對照上一本書看。
sun的hotspot虛擬機技術(shù):
http://java.sun.com/javase/technologies/hotspot/
BEA的JRockit虛擬機技術(shù):http://edocs.bea.com/jrockit/geninfo/genintro/index.html JVM技術(shù)文檔入口,虛擬機理論,內(nèi)存泄漏診斷等的索引頁。
IBM診斷資料:
http://www-128.ibm.com/developerworks/java/jdk/diagnosis/ 上面有一個500多頁的pdf文檔,對IBM JVM技術(shù)和診斷講解很深入。

    我不得不提的是,在查資料這塊,BEA和Sun都有很好的官方文檔和論壇支持,并且官方文檔導航非常好。雖然IBM的診斷資料也不少,但需要搜索,其搜索 是很痛苦的。而且,IBM官方論壇很差。如果用IBM的產(chǎn)品出問題,切記:找IBM技術(shù)支持,千萬不要蒙著頭搞!反正它們的產(chǎn)品很少免費。說實話,它們的 技術(shù)支持還是挺負責的,一般會為你推薦很多support資料,而該資料往往都在developerworks網(wǎng)站上,屬于support那個頻道,但你 就是搜不著。

Java虛擬機規(guī)范概要

    研究Java虛擬機,首先要了解Sun的Java虛擬機規(guī)范?,F(xiàn)在,該實現(xiàn)版本很多,如比較有名的Sun、IBM、BEA、Apple、HP、MS、 Apache Harmony。它們都實現(xiàn)了JVM規(guī)范,但有各自擴展。譬如,針對IBM虛擬機的堆碎片導致OutOfMemory(OOM),在Sun的虛擬機上就不 會發(fā)生。Sun的JVM有maxPermSize的概念,IBM就沒有,如果你設(shè)置這個參數(shù),虛擬機根本就啟動不了。

    比較有意思的是,學Java,就一定要了解各種規(guī)范,這和MS的風格很不一樣。Sun總是在定義一些規(guī)范,實現(xiàn)都留給各廠商。我們除了理解規(guī)范本身外,一 定要理解規(guī)范和實現(xiàn)之間的關(guān)系,譬如JDBC規(guī)范和JDBC驅(qū)動的關(guān)系,它們是怎么組合到一起的。要是你用過php的xml解析庫,或db函數(shù),就會體會 深刻,它們可沒有什么規(guī)范可言,所以每個數(shù)據(jù)庫廠商的db函數(shù)用法都不一樣。我推薦大家研讀一下HSQLDB的jdbc和Tomcat的servlet相 關(guān)實現(xiàn),因為我認為它們還是比較好懂的。

    JVM規(guī)范只是定義一個虛擬機該做什么,但它并沒有要求你該怎么做。例如我們最常見的Servlet規(guī)范,在該規(guī)范中,有 HttpServletRequest、HttpServletResponse,HttpSession等接口,但它們的實現(xiàn)都留給了各個容器廠商。遺 憾的是,規(guī)范留下的空白,會把我們這些開發(fā)人員給整慘了:容器間移植有時候就是惡夢。譬如J2EE并沒有SSO規(guī)范,但它很重要,我以前專門針對它做過 WebSphere AppServer和Weblogic AppServer的SSO項目,差別還是不小,不過還是有點共通,那就是都遵循JAAS規(guī)范。


JVM的結(jié)構(gòu)

    從功能上分,Java虛擬機主要由六個部分組成,可以分成三類:
第一類:JVM API:就是我們最常用的Java API,它是開發(fā)人員和Java交互的入口,它主要是JAVA_HOME/jre/lib下的運行時類庫rt.jar和編譯相關(guān)的tools.jar

第二類:JVM內(nèi)部組件
類裝載器(ClassLoader):將Byte Array的 .class文件裝載、鏈接和初始化。
內(nèi)存管理(Memory Managent):為對象分配內(nèi)存,以及釋放內(nèi)存。后者就是垃圾回收Garbage Collector(GC)。由于JVM最復雜的、最影響性能的就是GC,所以內(nèi)存管理一般就指垃圾回收。
診斷接口(Diagostics Interface):這主要體現(xiàn)在JVMTI(jdk1.4下的JVMPI和JVMDI),它主要用來診斷程序的問題和性能,一般提供給工具廠商實現(xiàn)。如eclispe IDE下的debug功能,Jprofiler性能調(diào)優(yōu)工具。
類解釋器(Interpreter):解釋裝載進虛擬機的class對象,包括JIT等特性相關(guān)。

第三類:平臺相關(guān)接口(Platform Interface):主要為了跨操作系統(tǒng)平臺重用JVM代碼,不過,它和我們開發(fā)人員關(guān)系不大。

    在以上六個組件中,我們開發(fā)人員最關(guān)心的是ClassLoaderGC, 用Java做系統(tǒng)框架、容器和它們密切相關(guān)。做業(yè)務(wù)系統(tǒng)時一些基礎(chǔ)代碼也和它們打交道,譬如最常用的Class.forName(), Thread.currentThread.getContextClassLoader()。我們仔細想想,為什么是上面兩個問題?因為,它和我們 class的整個生命周期最為相關(guān):怎么將一個class和相關(guān)class加載進來,class實例什么時候創(chuàng)建,什么時候被銷毀?
所以,下面的部分我們要專門討論這些問題。

ClassLoader

    JVM主要有三類ClassLoader:Bootstrap、Extention、Application,該三類ClassLoader從上到下是分級(hierarchy)結(jié)構(gòu),遵循代理模型(Delegation Model)。
Tip:大家可以看看sun.misc.Launcher的源碼,Bootstrap和Extention就在該文件里。該src可以在sun的網(wǎng)站上下載該壓縮包,約60M(jdk-1_5_0-src-scsl.zip),它不在jdk自帶的那個src.zip里。

    Bootstrap ClassLoader:也稱為primordial(root) class loader。主要是負責裝載jre/lib下的jar文件,當然,你也可以通過-Xbootclasspath參數(shù)定義。該ClassLoader不能 被Java代碼實例化,因為它是JVM本身的一部分。

    Extention ClassLoader:該ClassLoader是Bootstrap classLoader的子class loader。它主要負責加載jre/lib/ext/下的所有jar文件。只要jar包放置這個位置,就會被虛擬機加載。一個常見的、類似的問題是,你 將mysql的低版本驅(qū)動不小心放置在這兒,但你的Web應用程序的lib下有一個新的jdbc驅(qū)動,但怎么都報錯,譬如不支持JDBC2.0的 DataSource,這時你就要當心你的新jdbc可能并沒有被加載。這就是ClassLoader的delegate現(xiàn)象。常見的有l(wèi)og4j、 common-log、dbcp會出現(xiàn)問題,因為它們很容易被人塞到這個ext目錄,或是Tomcat下的common/lib目錄。

    Application ClassLoader:也稱為System ClassLoaer。它負責加載CLASSPATH環(huán)境變量下的classes。缺省情況下,它是用戶創(chuàng)建的任何ClassLoader的父 ClassLoader,我們創(chuàng)建的standalone應用的main class缺省情況下也是由它加載(通過Thread.currentThread().getContextClassLoader()查看)。
我們實際開發(fā)中,用ClassLoader更多時候是用其加載classpath下的資源,特別是配置文件,如ClassLoader.getResource(),比FileInputStream直接。

ClassLoader是一種分級(hierarchy)的代理(delegation)模型。
Delegation: 其實是Parent Delegation,當需要加載一個class時,當前線程的ClassLoader首先會將請求代理到其父classLoader,遞歸向上,如果該 class已經(jīng)被父classLoader加載,那么直接拿來用,譬如典型的ArrayList,它最終由Bootstrap ClassLoader加載。并且,每個ClassLoader只有一個父ClassLoader。
Class查找的位置和順序依次是:Cache、parent、self。
Hierarchy: 上面的delegation已經(jīng)暗示了一種分級結(jié)構(gòu),同時它也說明:一個ClassLoader只能看到被它自己加載的classes,或是看到其父 (parent) ClassLoader或祖先(ancestor) ClassLoader加載的Classes。
在一個單虛擬機環(huán)境下,標識一個類有兩個因素:class的全路徑名、該類的ClassLoader。

    我碰到的一個典型的例子是:在做WAS的SSO開發(fā)時,由于我們的類是由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。一般的應用開發(fā)用不到它,但我們最好理解。因為在內(nèi)存泄漏查找、應用程序部署出問題時,很多都和它有關(guān)。
譬 如,內(nèi)存泄漏是怎么產(chǎn)生的?這就涉及到ClassLoader和Class的生命周期。我曾經(jīng)碰到這樣一個問題:我們的程序用到了Webwork和 Spring框架,當部署到Tomcat下時沒有任何問題,但部署到WAS下,報告找不到Webwork的xml的DTD文件,而且Spring的日志也 總是失效。Why?因為解析xml dtd時,用的是IBM的Xerces,不是我們的。而Spring日志問題是因為應用程序用的是WAS的Common-log.jar,而不是我們的。 將應用的ClassLoader從默認的Parent-First,改成Parent-Last就可以解決,不過我們項目中用到其它庫,又發(fā)生了其它問 題。

一般來說,用到自定義ClassLoader有三種情況:
1、應用框架可以自己控制Classes的目錄,并且自動部署。
我讀過Jive公司的Wildfire(著名的即時通訊服務(wù)器),它自己有一套應用框架,非常靈活,遵循該框架插件規(guī)范的的第三方的plug-in放置在指定目錄可以自動部署,實現(xiàn)某些擴展功能,如文件傳輸、語音聊天。
2、區(qū)分用戶代碼
這被廣泛應用在Servlet容器和類似容器,譬如EJB Container設(shè)計中,大家看到Tomcat下有common、server、share三個目錄吧(ClassLoader順序從左到有),另外也有用戶應用的WEB-INF目錄,它是我們自己開發(fā)的。
3、允許Classes卸載
如 果沒有自定義的ClassLoader,那么我們自己應用中的classes永遠都不能被卸載,因為這些類被Application ClassLoader加載后cache起來了,我們的classes一直對該ClassLoader有引用,而該系統(tǒng)級的ClassLoader永遠都 不會被卸載,除非JVM shutdown了。JSP和Servlet的動態(tài)部署就用到這個特性。

待續(xù).......

Note: 還有JVM運行時(Runtime)架構(gòu),ClassLoader加載class過程沒有總結(jié),這兩部分我覺得太重要了,但內(nèi)容太多,寫不完啊。
這部分內(nèi)容,《Inside Java Virtual Machine》講解非常清楚,BEA的官方網(wǎng)站這部分也非常不錯,要理解深刻,我建議結(jié)合JProfiler工具,非常直觀。

待續(xù).......





【IT168 技術(shù)文檔】

    續(xù)寫這篇文章,已經(jīng)過去一個半月了。直到現(xiàn)在,系統(tǒng)一直運行平穩(wěn)。
先說說我接手這項工作的經(jīng)歷 吧:該項目大部分是06年10月就部署在客戶那邊了,到07年3月份,WAS宕機問題實在無法忍受,我才加入進來,前半年有另外一位同事斷斷續(xù)續(xù)處理,但 對問題一直都無可奈何,而且項目負責人也沒有引起足夠的重視??上攵?,最后付出的代價是非常慘重的。在這近半年的時間內(nèi),服務(wù)器宕機63次。每次宕機 時,WAS的JVM會dump出一個heapdump.phd文件(heap快照),然后JVM就死掉了,當然,此時WAS也停止了響應。一般我們的做法 是重啟,最后是干脆AIX每天晚上定時重啟。有時候一天還死多次。大家見附件的截圖(all-GC.png)。這是我接手后,用IBM的分析工具得到的截 圖。對截圖的分析,留給后面對應的部分吧。
服務(wù)器不穩(wěn)定、宕機問題,拖延到最后,客戶憤怒了,公司高層也害怕了,部門還專門成立了八人攻關(guān)組。當然了,我當時的壓力也非常大,因為我是技術(shù)負責人,也就是實實在在干活、想主意的。
服務(wù)器診斷那段時間,從前到后,我們也是沿著一條線走下來,雖然最后發(fā)現(xiàn)很多路都走不通?,F(xiàn)在就按這個思路,也就是時間先后一步步敘述吧。我想,大家如果也碰到類似應用服務(wù)器診斷,應該思路差不多。

術(shù)語說明:
IBM Websphere Application Server:WAS,WebSphere本身是一個平臺,產(chǎn)品家族
OutOfMemoryError:OOM,內(nèi)存泄漏,內(nèi)存溢出
Gabage Collection:GC,自動垃圾回收
Content Management System:CMS,就是給新聞類門戶網(wǎng)站編輯們用的系統(tǒng)

我們診斷大體上經(jīng)歷了以下幾個階段:
1、按Job調(diào)度線程池引起內(nèi)存泄漏診斷:因為很多次OOM是發(fā)生在某個特定時候,譬如14:30、22:40左右。
2、按應用程序引起內(nèi)存泄漏診斷:用JProfiler等工具探測:因為總是發(fā)生OOM。
3、分離WAS懷疑有OOM的應用:因為每個WAS應用太多,20來個,混一起沒法定位。
4、用IBM官方heap、GC分析工具。以及和IBM技術(shù)支持聯(lián)系。WAS、AIX參數(shù)優(yōu)化。
5、隔離出WAS超級惡魔程序:一個CMS產(chǎn)品。
6、WAS、AIX參數(shù)優(yōu)化、設(shè)置。

我們走到第5步時,才出現(xiàn)效果。計算一下,那時已經(jīng)過去一個月了。服務(wù)器宕機、系統(tǒng)不穩(wěn)定,在這個驗收的時候,客戶已經(jīng)忍無可忍,以致后來的每一次行動都得膽戰(zhàn)心驚得去做。

一、按Job調(diào)度線程池導致內(nèi)存泄漏診斷
    因為從我們WAS的日志(默認是native_stderr.log)來看,最近半年的宕機時間都有一個明顯時間規(guī)律。見附件截圖Job1-1.png。
    我想,做過Java服務(wù)器性能調(diào)優(yōu)的朋友,都知道在Web容器里面啟線程池是個不太好的做法,因為Web容器本身有一個線程池,譬如Servlet線程池 (Tomcat默認起25個),而自啟的線程池很容易導致Servlet線程管理混亂,最終導致GC問題。我們的現(xiàn)象似乎和那很符合。如果我們沿著這個思 路做下去,具體怎樣實施呢?

    我們的WAS上部署了20個左右的Web應用,譬如Lucene全文檢索、B2B行業(yè)數(shù)據(jù)同步等,都是通過Quartz的Job調(diào)度做的,當然還有很多其 它調(diào)度。當時,由我負責,通知相關(guān)負責人,將定時調(diào)度暫時去掉。觀察了幾天,后來發(fā)現(xiàn)問題依然存在,不過時間有點隨機了。
不過,最后還是發(fā)現(xiàn)OOM不是由Job調(diào)度引起的。
也 就是說,我們這個方案是失敗的。而且,我們的很多想法都是臆測的,沒有可靠的根據(jù),也沒有方向,再加上我是第一次處理這種問題,這導致后來查找問題的艱 難。但是,仔細想想,我們又能拿什么做依據(jù)呢?出現(xiàn)OOM錯誤,我想大多數(shù)人想到的,除了JVM參數(shù)設(shè)置,就是內(nèi)存泄漏。其實,OOM發(fā)生有很多種情況, 在IBM、Sun、BEA等不同虛擬機上,因為GC機制不一樣,所以原因一般都不同,容易定位難度也不一樣。下文會談到。
于是,我們干脆釜底抽薪分析問題吧:用JProfiler檢測。


二、按應用程序?qū)е聝?nèi)存泄漏診斷,JProfiler檢測

    如果遇到OOM問題,我想大家都會想到內(nèi)存檢測工具,現(xiàn)在最可靠的還是下面三種分析工具:Borland 的Optimizeit Suite,Quest的JProbe,ej-technologies的JProfiler。但面臨三個問題:
1、三個都是商業(yè)產(chǎn)品,公司暫時沒有買,必須自己下載,而且要找序列號。
2、工具必須支持AIX5.3+JDK1.42+WAS6.0,不是Windows平臺。
3、工具必須在客戶真實環(huán)境下部署,對客戶的業(yè)務(wù)不能有沖擊,也就是說部署測試工具前,必須做個大量測試,對工具非常熟練,遇到意外可以立即恢復現(xiàn)場。
Note:項目上線后,而不是測試或試運行階段遇到此類問題,非??简炄?;另外一個,就是性能和可伸縮性問題,很可能把整個項目給毀了。

    當我決定要這么做后,就立即動手查閱這些工具的官方文檔,用emule下載,最終都下載到了。試用后,最終選擇了JProfiler4.03,比起其它工 具,它界面美觀、清晰、功能強大、集成度高(Heap,Memory,CPU,Thread都統(tǒng)一了)。另外,JProbe沒有AIX版本,這也是放棄它 的一個原因。

    JVM的Profiler原理,都是通過JVM內(nèi)置的的標準C語言Profiler 接口收集數(shù)據(jù),然后通過Profiler工具的客戶端展現(xiàn)。也就是說各廠商的Profiler工具,都有兩個部分,一個部分是Profiler Agent,和JVM綁定,負責收集JVM內(nèi)部數(shù)據(jù),譬如方法調(diào)用次數(shù)、耗費時間等;另外一個部分就是Profiler front-end。通過Profiler工具的自定義local或remote協(xié)議傳輸?shù)絝ront-end,其實,我們最常用的JavaIDE的 debug功能就是在此基礎(chǔ)上的(JPDA)。(JProfiler的截圖http://www.ej-technologies.com/products/jprofiler/screenshots.html )。
下面是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

為了保證真實環(huán)境的檢測成功,我做了大量的試驗,譬如:
1、Windows系列的本地、遠程測試。
2、AIX的遠程測試。
3、Tomcat5.0、WebLogic8.14、WebSphere6.02,以及上述兩種方式的組合測試,排列組合,場景不下10個。
當時也參閱了大量JVM文檔,JProfiler官方幾百頁英文文檔,輔助的JProbe對照。而且也制造過內(nèi)存泄漏造成的OOM場景。
當然,要是在幾個月前,在客戶那邊部署的測試環(huán)境時,就進行測試該多好啊。

    在公司內(nèi)部,我用JProfiler測試了我們當時部署的幾個應用,沒有發(fā)現(xiàn)內(nèi)存泄漏,所以,我們最懷疑的是就是CMS系統(tǒng)。因為出問題的那個WAS上它 占去了90%的負荷(我們有多臺AIX、WAS服務(wù)器)。該CMS超級龐大,感覺著名的賽迪網(wǎng)就用它,當時該CMS廠商給我們部署都花了快一個月。所以再 重新部署一套測試環(huán)境也挺困難。另外,CMS提供商不給lisence。現(xiàn)在測試,客戶早就對我們惱火了,當然不怎么配合,這對我們工作的開展就有很大的 挑戰(zhàn)。

    在大致可以確定萬無一失的情況下,我們最終決定在客戶的真實環(huán)境下測試。也就是讓JProfiler的agent端直接在WAS的JVM里面啟動(北京IDC),然后遠程(大連)監(jiān)控。
本 來該模式在另外幾個應用的測試都通過了(因為北京IDC那邊好幾臺AIX服務(wù)器)。但一部署上,客戶的一些編輯用CMS時就感覺超級慢,盡管我們用了 JProfiler的最小負載模式。半個小時后,客戶實在無法忍受,打電話過來,又把我們部長和經(jīng)理訓了一頓,還要寫書面報告。我們被迫馬山中止測試,恢 復現(xiàn)場。

    雖然JProfiler也支持客戶那邊的環(huán)境,但還是有bug,至少負載一高就有嚴重的性能問題,幾乎導致系統(tǒng)掛起,這是我當時沒有料到的。 JProfiler一啟動,WAS的啟動時間就由原來的3分鐘降到10分鐘,而且系統(tǒng)響應明顯變慢,我們具體的環(huán)境如下(排列組合恐怕不下20種):
1、AIX5.3,Power PC64位(不是32位)
2、WebSphere6.0
3、IBM JVM1.42
4、Remote 模式

    我后來仔細讀了一下JProfiler的changeLog,發(fā)現(xiàn)對上面的環(huán)境支持不夠也很正常,因為官方在最近的4.3.x版本下陸續(xù)有對IBM JVM和Websphere6.0的features和bug fix:http://www.ej-technologies.com/download/jprofiler/changelog.html

    進行到這一步,我忽然覺得無計可施了 ,此時已經(jīng)過了三周。
上面的策略,我認為是很正統(tǒng)的處理方法。誰怪我們當初項目上線時沒有進行充分的測試呢?其實,這類測試沒做也正常,OOM這類問題誰都無法預測。

到這個時候,我想肯定有人會問我?你知道導致JVM的OOM有幾種情況嗎?在當時,我想到了以下五種:
1、JVM的heap最小、最大值沒有設(shè),或不合理。
2、JVM的maxPermSize沒有設(shè)置(這個IBM的JVM沒有,一設(shè)置JVM就起不來)。
對于Sun和BEA的JVM,以上兩種參數(shù)設(shè)置,可以排除90%以上的非程序內(nèi)存溢出導致的OOM。
3、程序內(nèi)存泄漏
4、有的JVM,當在80%的CPU時間內(nèi),不能GC出2%的heap時,也發(fā)生OOM(IBM的JVM就是,但我沒有驗證)
5、JVM本身內(nèi)存泄漏(JVM有bug不是不可能)

    現(xiàn)在的難題是,如果是那個可怕的CMS程序本身有內(nèi)存溢出,在產(chǎn)品環(huán)境下,我們怎么去驗證?我們自己開發(fā)的10來個web應用,測試并不是很難,在公司測 試都可以。但是,我現(xiàn)在最想解決的,就是CMS測試的問題。而且,在我們那種環(huán)境下,該CMS產(chǎn)品供應商并沒有透露成功案例。

其實,最后發(fā)現(xiàn),并不是內(nèi)存泄漏造成的,因為我們的heap走勢都很平穩(wěn)。納悶的是,有1000M的heap,每次在heap只被占用400來M時就發(fā)生OOM,沒有任何預兆。大家猜猜,會是什么原因 ?這個問題我到后面相關(guān)章節(jié)再說吧。

既然我們所有的矛頭都指向那個可怕的CMS系統(tǒng),現(xiàn)在就是考慮隔離的問題。如果我們驗證這個問題是CMS造成的, 那么大部分責任就應該由CMS廠商承擔
既然CMS我們不敢移(費勁,而且客戶在正式用),那我就移我們開發(fā)的10來個web系統(tǒng)吧。

三、移出除CMS系統(tǒng)以外的所有應用
說起來容易啊,做呢? 隔離(移動)工作由我負責,具體涉及到10來個相關(guān)負責人。
轉(zhuǎn)移工作,必須處理好很多問題,就說幾個印象最深的吧:
1、某些應用,如Blog和BBS,都涉及到文件、圖片上傳目錄和產(chǎn)品本身的環(huán)境,如 JDBC連接池、Cache位置。
2、目標服務(wù)器本身的環(huán)境,WAS安裝環(huán)境、網(wǎng)絡(luò)等。
3、移植時的先后順序、調(diào)度,各應用內(nèi)部本身的約束關(guān)系。
4、移植后的測試。
當然,還有一個最嚴峻的問題,客戶允許我們這么做嗎?對它們目前運行的系統(tǒng)有多大影響?風險如何評估?

    這個工作持續(xù)了一天,已經(jīng)完成了80%的工作,到第二天,客戶又惱火了:WAS又宕機了。
為什 么?這確實是WAS的一個bug:WAS的后臺隨便一操作,heap就會突然上升幾百M,導致JVM內(nèi)存不夠。不過WAS撐住的話,過半小時后就會降下 來,我估計是WAS后臺對用戶操作狀態(tài)、文件都緩存到Session里面。你們可以檢查類似這樣的一個文件夾:d:\IBM\WebSphere\ AppServer\profiles\AppSrv01\wstemp,我不知道為什么WAS不主動去清除它,它偷偷的就上升到幾個G,系統(tǒng)硬盤可能不 久就后就會空間不足,WAS莫名遲緩、最后死掉。聽過WAS6.0以前這個目錄更夸張。大家見我附件的截圖WAS_Console.png那個尖峰。

咋辦?經(jīng)理也已經(jīng)不敢讓我們繼續(xù)鋌而走險了。這個方案最終又以失敗告終 。

    不過,最后我們還是發(fā)現(xiàn)問題出在CMS上。我們以前把這個問題向CMS技術(shù)支持反映,有大量依據(jù)和現(xiàn)象,并且把相關(guān)日志都給它們。過了兩天,他們最后竟然只回了一句話“從給我的兩個日志來看,沒有找到任何與XXX有關(guān)的東西....”。TMD!我真的很生氣 ,它們的產(chǎn)品都折磨我們半年之久了。不過,看他們對IBM的WAS和JVM也不懂,我也就不想再說什么了。下面是我的郵件,公司機密部分都隱去了:

引用
    附件是我們這段時間服務(wù)器宕機的日志。我們用IBM Pattern Modeling and Analysis Tool for Java Garbage Collector Version 1.3.2分析了一下虛擬機日志,沒有發(fā)現(xiàn)是內(nèi)存泄漏導致;用IBM HeapAnalyzer Version 1.4.4 分析heap文件,也沒有發(fā)現(xiàn)很可疑的內(nèi)存泄漏。

    我想以前你們也這樣做過,現(xiàn)在我們分析錯誤日志,發(fā)現(xiàn)有一個現(xiàn)象,在宕機時,總是找不到文件,我看就是Websphere或是AIX IO資源不夠,不知道是什么導致的。但是,我們自己的應用,基本上沒有什么IO,除了一次load幾個配置文件。不過,我覺得你們WCM的IO操作挺多 的,不知道你對日志有什么新的發(fā)現(xiàn)。
客觀的說,這幾個月來,宕機那臺服務(wù)器,除了你們的XXX,就以論壇和blog為主,而且他們都是開源的。在頻繁宕機的06年11月份,我們的論壇和blog還沒有上線。現(xiàn)在我們不得不每天晚上11點定時重啟,但這也不是長久之計。
現(xiàn)在,我們進行分離遇到很大阻力,原來想把你們的XXX單獨分離出來,在當前的環(huán)境下,不是很現(xiàn)實,如安裝、測試(負載、定時服務(wù)),所以現(xiàn)在分離我們自己的應用,但當前在產(chǎn)品環(huán)境下,客戶方阻力也很大。
希望盡快能夠得到你們的問題建議和方案。

    文中說到了IBM的兩個分析工具,這也是我們后來的救星:我們就是需要這種離線分析工具,因為實時檢測已經(jīng)證明不現(xiàn)實。但我始終對該分析出來的結(jié)果抱懷疑態(tài)度,直到我去深入IBM的JVM以及和IBM的技術(shù)支持交流......

柳暗花明啊 ,至少看到了一點希望,不過最后我們還是失望而返。


四、用IBM的HeapAnalyzer和GarbageCollector檢測

    找到這兩個工具,已經(jīng)是夠費勁了,因為以前找的IBM HeapRoot工具,讓我對這類工具很失望。而且,這兩個工具,只有在IBM的Techinical Support網(wǎng)站能夠搜索到,但很不容易,因為那兩個工具,并不是象IBM的Websphere產(chǎn)品那樣宣傳,它只在IBM Techinical Support文章的某些角落里出現(xiàn)。要知道,Techinical Support是IBM很重要的收入來源,這類文檔,他們并不會讓你很輕易就拿到,比起B(yǎng)EA WLS的支持網(wǎng)站dev2dev差遠了。
具體 診斷細節(jié)我就不詳述了。我認為,IBM的WAS或JVM出了性能和OOM問題,這兩個工具是最有效的,而且是離線分析工具,比起那些實時Profiler 工具,某些場合有絕對的優(yōu)勢:譬如我們目前的產(chǎn)品環(huán)境,你只能分析宕機后的日志,實時分析前面已經(jīng)驗證是不可行的。
從日志分析,我們最終得出結(jié)論,我們購買的CMS系統(tǒng)有嚴重的碎片(大對象)問題,而該問題是OOM的罪魁禍首,而且IBM工程師也得出了同一結(jié)論。不過,在起先我們得出這一結(jié)論一周后,我還始終不相信heap碎片會導致OOM,直到IBM工程師總是向我強調(diào)。

    我想很多人也是不太相信,因為大多數(shù)人用的都是Sun的JVM,譬如Windows、Solaris上的hotspot。而且,Sun JVM出問題,如果是配置的問題,一般通過配置heap最大最小值,以及maxPermSize都可以解決。Heap碎片導致的OOM,只有BEA的 JRockit和IBM JVM上發(fā)生,不過JRockit有專門文檔說明,而且很容易找到(就在jdk的文檔里面)。

    配置heap最小最大值,我想大多數(shù)人都有經(jīng)驗。對于Sun的JVM來 說,一般可以設(shè)置heap最大最小值一致,也是推薦的做法。因為它的GC策略默認是復制、分代算法。也就是說,它會將heap分成不同的幾個區(qū),譬如 Solaris JVM中最上面有兩個大小相等的區(qū)。GC時刻,將一個區(qū)的存活對象復制到另外一個對等區(qū),垃圾對象就算遺棄了。這樣在heap里面,就不存在碎片問題。另 外,根據(jù)Java對象的存活期不同,將之轉(zhuǎn)移到不同的區(qū)(Tenured區(qū)),存活最長的在最底部(火車算法),這也就是分代模型。具體請參考官方文檔:http://java.sun.com/docs/hotspot/gc1.4.2/

    對于maxPermSize(Permanent Generation),主要和那些加載到JVM里面的Java Class對象相關(guān),它的空間不是在Java Heap里面分配。如果你當前的heap有1000M,permSize是200M,那么JVM至少占用1200M。
在 這個空間內(nèi)的對象的生存期和JVM是一樣的,譬如JDK的核心類庫,它們被System Classloader加載到JVM的Method Area(方法區(qū))后,就不會被GC掉的,這些對象一般是Class對象,而不是普通的實例對象,也就是JVM的元數(shù)據(jù)。我們在用反射時經(jīng)常用到它們。所 以,對于現(xiàn)在象Spring、Hibernate這些框架經(jīng)常通過反射創(chuàng)建實例,可能對maxPermSize要求就大了,缺省的64M很多時候是不夠 的,特別是對于應用服務(wù)器里的應用,象JSP就會產(chǎn)生和加載很多classes。不過,如果是它導致的OOM,一般會有類似 perm size提示。

    但是,對于IBM的JVM,情況就完全不一樣。它的默認GC策略并沒有采取復制、分代。這個可以從GC日志分析出來。它不像Sun的JVM那樣,有個單獨 的方法區(qū),它的方法區(qū)就放在Java Heap里面。JVM規(guī)范里面并沒有要求方法區(qū)的必須存放的位置,因為它只是一個JVM實現(xiàn)問題。

    在IBM的JVM里面,這些對象一般分配在稱為k-cluster和p-cluster里(cluster又是屬于Heap),而后者一般是臨時在 heap里面申請。并且,這些cluster是不能GC,或是被移動重排的(Compact過程)。這就導致Java Heap里面就如同馬蜂窩,但不同的蜂孔又不能合并,于是,當我們程序里面產(chǎn)生一個大對象,譬如2M的數(shù)組(數(shù)組必須分配在連續(xù)的內(nèi)存區(qū))時,就沒有可分 配空間了,于是就報告OOM。這些不能被移動的cluster就稱為所謂的碎片。此時,JVM的Heap利用率可能不到50%。
當然,通過一定時期的GC日志,可以計算出cluster的合理大?。▽iT在Java Heap的底部),另外,還可以為這些大對象專門分配大對象區(qū)的(超過64k的對象)。

    通過上面的理論介紹,我想大家一定知道了為什么IBM的JVM里面不推薦heap的最大最小值相同,因為這樣碎片問題會非常嚴重:如果我們每次大對象申請 內(nèi)存時,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 技術(shù)支持告訴我的,太重要了?。?
http://www-900.ibm.com/cn/support/viewdoc/detail?DocId=2847476B08000

    我想大家應該會問:我怎么能夠肯定我的OOM問題是heap碎片造成的呢?下面的方法可以驗證。
在OOM 發(fā)生時,JVM會產(chǎn)生一個heapdump文件。然后用GarbageCollector分析出該OOM發(fā)生時刻,JVM去申請的空間,譬如約235k。 此時,你再用HeapAnalyzer去分析此時的heap快照里面的gap size大?。障洞笮。┖透髯缘目捎脭?shù)目。你會發(fā)現(xiàn),大于235k的空隙個數(shù)為0。這就是碎片導致OOM的證據(jù)。

另外,有人會問:我懷疑我的OOM是因為程序內(nèi)存泄漏造成的,怎么去驗證

    你可以用HeapAnalyzer分析發(fā)生OOM時刻的heap快照,工具會羅列出哪些對象懷疑有內(nèi)存泄漏,譬如Cache對象都非常大(但你可以確定它 不是內(nèi)存泄漏)。另外,分析這次宕機(從這次虛擬機啟動到宕機這段時間)的heap走勢,如果曲線明顯是向上傾斜,也就是那種典型的內(nèi)存泄漏圖,就有可能 是內(nèi)存泄漏。當然,還必須結(jié)合heap快照。
內(nèi)存持續(xù)上升在JVM開始一段時間很正常,因為JVM對第一次訪問到的Class 對象,譬如一個典型的Web應用,就有jdk的class、Spring或Hibernate的class對象,它們都會被緩存下來 (ClassLoader原理),一般均不會被GC。當大多數(shù)class對象緩存差不多(當然還可能有一些Singleton對象,不過不怎么占分量), JVM的Heap就平穩(wěn)了,呈一水平波浪或鋸齒線。
如果可以用JProfiler這類工具實時監(jiān)控,就更容易確診了。

經(jīng)過一番周折,我們終于看到了一線希望了 。

    在一定的準備后,我們決定對WAS進行性能調(diào)優(yōu)了。WAS的調(diào)優(yōu)參數(shù),可以分為兩個部分:JVM級別和WAS級別:
JVM:主要是GC和Heap。
WAS:Thread Pool,JDBC DataSource。
當然要調(diào)節(jié),你需要明白你的目標是什么,調(diào)節(jié)依據(jù)是什么,怎么計算,絕對不是憑空想象的,譬如heap最小值1024M,日志證明,該參數(shù)非常不適合我們的環(huán)境。具體細節(jié),留給后文吧。

戰(zhàn)戰(zhàn)兢兢地,中午12:00,我們給產(chǎn)品環(huán)境下的WAS調(diào)節(jié)參數(shù)、重啟,同時優(yōu)化了AIX的IO相關(guān)參數(shù)。

    我試著設(shè)置了一下JVM的k-cluster和p-cluster。下午15:00左右,WAS掛了,AIX也掛了。這下麻煩可大了。我們都慌了,馬山客 戶的老總就來電話了,一陣嘩嘩啦啦。實在無奈,讓客戶那邊工作人員通知機房(服務(wù)器托管處)工作人員重啟AIX。我也不得不強行更改剛才的參數(shù),立即設(shè)為 另外一個值。
其實,我把那個兩個cluster值確實設(shè)置太大了,我把它們設(shè)置為推薦值的5倍,譬如p-cluster是65k×110%×5。另外一個愚蠢的設(shè)置就是把最小heap設(shè)置為2048M(AIX有4G內(nèi)存)。
后來我恢復到約正常的值,也就是去掉那個cluster的5,另外分配了一個30%的大對象區(qū)(如果1000M的heap,就是700M+300M)。

就這樣,系統(tǒng)持續(xù)正常運行了三天,以前可是一天一down。當在三天后再次宕機時,我們都沒有自信了 。不得不通過AIX的cron,繼續(xù)每天深夜11點的WAS定時重啟。
不過,那次宕機,包括以后的幾次宕機,再也沒有出現(xiàn)OOM錯誤了,但系統(tǒng)依然不穩(wěn)定。雖然我可以說OOM問題解決了,但領(lǐng)導和客戶需要的并不是這種結(jié)果。

其實,在這個時候,我們已經(jīng)發(fā)現(xiàn)我們系統(tǒng)的四大問題:
1、WAS和JVM參數(shù):OOM問題
2、AIX的IO和Paging Spacing不足:AIX日志后來顯示錯誤
3、AIX的WAS分區(qū)空間不夠:WAS的日志膨脹一周就把那個opt分區(qū)塞滿了。
4、應用程序的JDBC連接池:我們20來個應用,一個20 connections,DB2數(shù)據(jù)庫有時被撐死。

也就是說,我們最初在客戶那兒部署時,用的默認值根本不行。而且,部署涉及多人,人員之間出現(xiàn)斷層。如果我們只是按OOM,無疑是走入死胡同,必須全局考慮!
但 是,項目組實力薄弱,公司范圍內(nèi)就沒有對AIX精通的。不過項目組原來有一個搞銀行系統(tǒng),在AIX下開發(fā),就他熟悉些。我當時對AIX也比較陌生,你們從 Linux轉(zhuǎn)到AIX,你就知道它有多別扭了。命令都自定一套(也許因為是Unix元老吧),那個shell也超級別扭,而且參考書特少。不是自詡,我兩 年前負責一個高負載的Linux服務(wù)器管理一年多,也是玩得很轉(zhuǎn)的。

就這樣,他負責AIX的相關(guān)問題,我負責WAS相關(guān)的。
但是,現(xiàn)實環(huán)境,已經(jīng)不允許我們再試驗下去了 。我們必須找到一條絕對可靠的對策!
這就是下文的CMS系統(tǒng)大遷移,服務(wù)器再次優(yōu)化。


五、隔離CMS系統(tǒng),服務(wù)器優(yōu)化
    從前面的介紹,大家應該記得,我們開始是固定CMS,分離其它應用,但遭遇失敗。現(xiàn)在是反過來,干脆把CMS系統(tǒng)趕出WAS平臺 。

說實話,項目經(jīng)理做這個決定,我認為已經(jīng)是鼓出很大勇氣了 。

    當時我們想在一個備用AIX機上安裝CMS產(chǎn)品測試,但最后還是沒有做成:
CMS這類文章發(fā)布系統(tǒng)很難安裝,也不好測試,又沒有l(wèi)iscence,而且還有一堆準備工作。絕對沒有著名的openCMS安裝那么簡單,當然功能遠遠比它復雜。而且,我們當時也低估了后來的工作,總覺得問題好解決。

    在很遙遠的06年中期,CMS廠商在客戶那邊一臺AIX的Tomcat上部署了一套CMS產(chǎn)品。但當時客戶執(zhí)意要求將其跑在WAS上,也就是現(xiàn)在的情況。 最開始,客戶還要求我們必須用WAS的集群(我們買的就是WAS的ND版),無奈該CMS不支持。要是集群,又是死傷一遍。其實,現(xiàn)在想想,我們當時太被 動,CMS這種東西,就供公司的幾十個編輯用,一個普通Tomcat就完全夠用。而且,把它和面向公網(wǎng)的Internet應用混在一起,完全沒有必要。也 許,被動是因為我的實力造成的。

我們決定背水一戰(zhàn)時,已經(jīng)做過周密的計劃:某年某月某日晚上8:00......
CMS產(chǎn)品負責人現(xiàn)場切換
xx(我)負責WAS相關(guān)參數(shù)調(diào)整
yy負責AIX參數(shù)。
zz負責應用的測試
…..

總之,該行動涉及到客戶方、產(chǎn)品提供商、公司高層、項目組。每個人都密切關(guān)注,不下20人。每個人都守在電腦前,隨時聽候調(diào)遣,當天晚上,我們都沒有準備回家睡覺,大家齊心協(xié)力。

真沒想到,整個式切換工作,一個小時就順利完成 !第二天,客戶編輯打開瀏覽器,她們一定想不到昨晚大家準備經(jīng)歷一場廝殺….

系統(tǒng)持續(xù)平穩(wěn)地運行了一周,然后是漫長的五一,我回湖北黃岡老家休息了八天?;貋頃r,一切依舊。

當天晚上,我們這邊主要做了兩項工作:
1、JVM的Heap參數(shù),共五個。
2、AIX的IO、Paging Space等共六個。
當然還有其他人的工作,譬如測試、監(jiān)控。

    還有一個非常重要的方面:JDBC連接池。我們原來是在每個Web應用里面獨立設(shè)置,這樣20來個應用就有幾百個DB連接,一不小心DB就給撐死?,F(xiàn)在統(tǒng) 一交由WAS內(nèi)置DataSource處理,總共連接不到30個。其實,我們項目開始部署時,就是這樣做的,但當時WAS內(nèi)置的DataSource對 JTA(XA)支持有bug (這個和IBM技術(shù)支持確認過,但他們沒有給予很好的解決方案),不過Datasource還是配好的。

    但是這個工作已經(jīng)屬于WAS性能優(yōu)化的主題了,而且優(yōu)化值必須持續(xù)觀察一段時間,通過專門的分析工具來計算。
優(yōu)化本身,是一項很考驗人的工作,我就簡單說一下最實用方法吧,也許是專門針對IBM的產(chǎn)品。
1、清理歸零WAS日志。然后啟動WAS,生成日志(-verbose:gc默認是開的)
2、讓WAS持續(xù)運行約兩周,讓JVM Heap占用曲線平穩(wěn)一段時間即可(用IBM的Garbage Collector分析觀察)。
3、 在AIX的shell里,產(chǎn)生heapdump.phd文件,也就是heap快照。命令:kill -3 pid (pid是WAS的PID,通過ps –ef查看),觀察heap當時的碎片情況,是否需要單獨分大對象區(qū)(一般不需要設(shè)置),特別是那個方法區(qū)Class對象大小(p-cluster參 數(shù))。
4、通過GC工具,觀察GC平均時間、Heap實際占用情況。Note: GC是一個Stop The World過程,也就是說GC時系統(tǒng)對外不響應,多CPU也不例外??茨愕膽脤嶋H需求了,GC持續(xù)時間和頻率是矛盾的,另外還有性能考慮。一般Web應 用,我想讓GC持續(xù)時間(Pause time)調(diào)節(jié)到合理值就ok了,譬如0.2到0.4s。
5、根據(jù)3可以算出k-cluster值,它是工具推薦值的110%。
6、Heap的最小值是程序剛啟動不久的占用值,譬如320M。切記:IBM JVM初始值太大非常不好。
7、 Heap的最大值是系統(tǒng)平穩(wěn)后的100/70。也就是說如果最大值是1000M,那么應該平穩(wěn)時是700M,還有30%的空余。IBM的JVM默認情下的 碎片問題,WAS控制臺下操作Heap猛增這種bug,你不得不堤防。Heap最大值不設(shè),AIX下的WAS肯定OOM。

當然啦,我沒有考慮到大對象區(qū)的計算(雖然我們的應用設(shè)置了專門的大對象區(qū)),包括IBM JVM支持的分代GC、并行GC,Heap每次expand百分比等。那些情況我們一般不常用,譬如,你的AIX平臺一般不是16CPU吧?

一口氣寫到現(xiàn)在,我忽然覺得該收尾了。下面就說說我對這類工作的整體看法吧。
1、盡量在項目測試和試運行的時候就進行壓力、性能測試,當正式投入使用后,如果發(fā)現(xiàn)類似問題,代價非常大。躲過算你運氣好,一般來說,可能你們系統(tǒng)沒多少人用,也不是核心業(yè)務(wù)系統(tǒng),譬如一般的電子政務(wù)。
2、千萬不要低估了技術(shù)風險,用IBM的系列產(chǎn)品尤其要慎重,出問題一定不要忘了技術(shù)支持。而且,查資料時,建議用google English,因為象WebSphere這類問題,很少有中文資料。
3、程序部署環(huán)境建立時,就要考慮到日后的正式環(huán)境,譬如AIX的Paging Space、IO、分區(qū)大小,默認值往往是不行的,而且在產(chǎn)品環(huán)境下改這些值,往往非常難。
4、 在項目開發(fā)初期,就考慮到日志的問題,因為它分散到每個方法內(nèi),必須慎重定義好debug、info、warn、error級別,不要隨便忽視異常 (catch里面不記錄),到真正程序出問題時,它就是我們的最重要的依據(jù)之一。當然這主要是功能性問題診斷。另外對于高負載網(wǎng)站,日志文件往往非常大, 各級別日志千萬不要混在一起,否則找問題就很困難了。
5、怎么說呢,別死扣技術(shù),以為什么都可以通過技術(shù)解決。你看我們最大的問題就是把 CMS給移到Tomcat下。你要是問我,為什么CMS產(chǎn)品會導致系統(tǒng)這么多的問題,我也不知道,到那時候,我確實也不想深入。我只要知道,趕出你這個應 用,我的系統(tǒng)就好控制多了。而且,那個CMS系統(tǒng),在Tomcat下,就是跑得服服帖帖的,非常穩(wěn)定。難度是可惡的WAS?不過那CMS,據(jù)IBM工程 師,包括我們二次開發(fā),都覺得夠爛了,每個jsp頁面都打開、關(guān)閉DB connection(7年前的jsp開發(fā)模式),還有那么嚴重的大對象問題。

    好了,以上總結(jié)的幾點可能也不充分、深入,但如果你仔細讀我這篇文章,應該有自己的想法。畢竟,只有經(jīng)過思考的東西,才會屬于自己。