<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 2,  comments - 0,  trackbacks - 0

    JAVAEE 常見(jiàn)性能問(wèn)題解決手冊(cè)
    內(nèi)存溢出錯(cuò)誤

    最常見(jiàn)的折磨著企業(yè)級(jí)應(yīng)用程序的錯(cuò)誤是讓人恐懼的outofmemoryError(內(nèi)存溢出錯(cuò)誤)
    這個(gè)錯(cuò)誤引起下面這些典型的癥狀:
    ----應(yīng)用服務(wù)器崩潰
    ----性能下降
    ----一個(gè)看起來(lái)好像無(wú)法結(jié)束的死循環(huán)在重復(fù)不斷的執(zhí)行垃圾收集,它會(huì)導(dǎo)致程序停止運(yùn)行,并且經(jīng)常導(dǎo)致應(yīng)用服務(wù)器崩潰
    不管癥狀是什么,如果你想讓程序恢復(fù)正常運(yùn)行,你一般都需要重新啟動(dòng)應(yīng)用服務(wù)器。

    引發(fā)out-of-memory 錯(cuò)誤的原因
    在你打算解決out-of-memory 錯(cuò)誤之前,首先了解為什么會(huì)引發(fā)這個(gè)錯(cuò)誤對(duì)你有很大的幫助。如果JVM里運(yùn)行的程序, 它的內(nèi)存堆和持久存儲(chǔ)區(qū)域的都滿(mǎn)了,這個(gè)時(shí)候程序還想創(chuàng)建對(duì)象實(shí)例的話(huà),垃圾收集器就會(huì)啟動(dòng),試圖釋放足夠的內(nèi)存來(lái)創(chuàng)建這個(gè)對(duì)象。這個(gè)時(shí)候如果垃圾收集器沒(méi)有能力釋放出足夠的內(nèi)存,它就會(huì)拋出OutOfMemoryError內(nèi)存溢出錯(cuò)誤。

    Out-of-memory錯(cuò)誤一般是JAVA內(nèi)存泄漏引起的?;貞浬厦嫠懻摰膬?nèi)容,內(nèi)存泄漏的原因是一個(gè)對(duì)象雖然不被使用了,但是依然還有對(duì)象引用他。當(dāng)一個(gè)對(duì)象不再被使用時(shí),但是依然有一個(gè)或多個(gè)對(duì)象引用這個(gè)對(duì)象,因此垃圾收集器就不會(huì)釋放它所占據(jù)的內(nèi)存。這塊內(nèi)存就被占用了,堆中也就少了塊可用的空間。在WEB REQUESTS中這種類(lèi)型的的內(nèi)存泄漏很典型,一兩個(gè)內(nèi)存對(duì)象的泄漏可能不會(huì)導(dǎo)致程序服務(wù)器的崩潰,但是10000或者20000個(gè)就可能會(huì)導(dǎo)致這個(gè)惡果。而且,大多數(shù)這些泄漏的對(duì)象并不是象DOUBLE或者INTEGER這樣的簡(jiǎn)單對(duì)象,而可能是存在于堆中一系列相關(guān)的對(duì)象。例如,你可能在不經(jīng)意間引用了一個(gè)Person對(duì)象,但是這個(gè)對(duì)象包含一個(gè)Profile對(duì)象,此對(duì)象還包含了許多擁有一系列數(shù)據(jù)的PerformanceReview對(duì)象。這樣不只是丟失了那個(gè)Person對(duì)象所占據(jù)的100 bytes的內(nèi)存,你丟失了這一系列相關(guān)對(duì)象所占據(jù)的內(nèi)存空間,可能是高達(dá)500KB甚至更多。

    為了尋找這個(gè)問(wèn)題的真正根源,你需要判斷是內(nèi)存泄漏還是以OutOfMemoryError形式出現(xiàn)的其他一些故障。我使用以下2種方法來(lái)判斷:
    ----深入分析內(nèi)存數(shù)據(jù)
    ----觀(guān)察堆的增長(zhǎng)方式

    不同JVMJAVA虛擬機(jī))的調(diào)整程序的運(yùn)作方式是不相同的,例如SUNIBMJVM,但都有相同的的地方。

    SUN JVM的內(nèi)存管理方式
    SUNJVM是類(lèi)似人類(lèi)家族,也就是在一個(gè)地方創(chuàng)建對(duì)象,在它長(zhǎng)期占據(jù)空間之前給它多次死亡的機(jī)會(huì)。
    SUN JVM會(huì)劃分為:
    1 年輕的一代(Young generation),包括EDEN2個(gè)幸存者空間(出發(fā)地和目的地the From space and the To space
    2 老一代(Old generation)
    3 永久的一代(Permanent generation)

    1   解釋了SUN 堆的家族和空間的詳細(xì)分類(lèi)


    對(duì)象在EDEN出生就是被創(chuàng)建,當(dāng)EDEN滿(mǎn)了的時(shí)候,垃圾收集器就把所有在EDEN中的對(duì)象掃描一次,把所有有效的對(duì)象拷貝到第一個(gè)幸存者空間,同時(shí)把無(wú)效的對(duì)象所占用的空間釋放。當(dāng)EDEN再次變滿(mǎn)了的時(shí)候,就啟動(dòng)移動(dòng)程序把EDEN中有效的對(duì)象拷貝到第二個(gè)幸存者空間,同時(shí),也將第一個(gè)幸存者空間中的有效對(duì)象拷貝到第二個(gè)幸存者空間。如果填充到第二個(gè)生存者空間中的有效對(duì)象被第一個(gè)生存者空間或EDEN中的對(duì)象引用,那么這些對(duì)象就是長(zhǎng)期存在的(也就是說(shuō),他們被拷貝到老一代)。若垃圾收集器依據(jù)這種小幅度的調(diào)整收集(minor collection)不能找出足夠的空間,就是象這樣的拷貝收集(copy collection),就運(yùn)行大幅度的收集,就是讓所有的東西停止(stop-the-world collection)。運(yùn)行這個(gè)大幅度的調(diào)整收集時(shí),垃圾收集器就停止所有在堆中運(yùn)行的線(xiàn)程并執(zhí)行清除動(dòng)作(mark-and-sweep collection),把新一代空間釋放空并準(zhǔn)備重啟程序。

    2和圖3展示的是了小幅度收集如何運(yùn)行


    2。對(duì)象在EDEN被創(chuàng)建一直到這個(gè)空間變滿(mǎn)。


    3。處理的順序十分重要:垃圾收集器首先掃描EDEN和生存者空間,這就保證了占據(jù)空間的對(duì)象有足夠的機(jī)會(huì)證明自己是有效的。


    4展示了一個(gè)小幅度調(diào)整是如何運(yùn)行的

    4:當(dāng)垃圾收集器釋放所有的無(wú)效的對(duì)象并把有效的對(duì)象移動(dòng)到一個(gè)更緊湊整齊的新空間,它將EDEN和生存者空間清空。

    以上就是SUN實(shí)現(xiàn)的垃圾收集器機(jī)制,你可以看出在老一代中的對(duì)象會(huì)被大幅度調(diào)整器收集清除。長(zhǎng)生命周期的對(duì)象的清除花費(fèi)的代價(jià)很高,因此如果你希望生命周期短的對(duì)象在占據(jù)空間前及時(shí)的死亡,就需要一個(gè)主垃圾收集器去回收他們的內(nèi)存。

    上面所講解的東西是為了更好的幫助我們識(shí)別出內(nèi)存泄漏。當(dāng)JAVA中的一個(gè)對(duì)象包含了一個(gè)并不想要的一個(gè)指向其他對(duì)象的引用的時(shí)候,內(nèi)存就會(huì)泄漏,這個(gè)引用阻止了垃圾收集器去回收它所占據(jù)的內(nèi)存。采用這種機(jī)制的SUN 虛擬機(jī),對(duì)象不會(huì)被丟棄,而是利用自己特有的方法把他們從樂(lè)園和幸存者空間移動(dòng)到老一代地區(qū)。因此,在一個(gè)基于多用戶(hù)的WEB環(huán)境,如果許多請(qǐng)求造成了泄漏,你就會(huì)發(fā)現(xiàn)老一代的增長(zhǎng)。

    5顯示了那些潛在可能造成泄漏的對(duì)象:主收集器收集后遺留下來(lái)占據(jù)空間的對(duì)象會(huì)越來(lái)越多。不是所有的占據(jù)空間的對(duì)象都造成內(nèi)存泄漏,但是造成內(nèi)存泄漏的對(duì)象最終都占據(jù)者空間。如果內(nèi)存泄漏的確存在,這些造成泄漏的對(duì)象就會(huì)不斷的占據(jù)空間,直至造成內(nèi)存溢出。

    因此,我們需要去跟蹤垃圾收集器在處理老一代中的運(yùn)行:每次垃圾收集器大幅度收集運(yùn)行時(shí),有多少內(nèi)存被釋放?老一代內(nèi)容是不是按一定的原理來(lái)增長(zhǎng)?


    5。陰影表示在經(jīng)過(guò)大幅度的收集后幸存下來(lái)的對(duì)象,這些對(duì)象是潛在可能引發(fā)內(nèi)存泄漏的對(duì)象

    一部分這些相關(guān)的信息是可以通過(guò)跟蹤API得到,更詳細(xì)的信息通過(guò)詳細(xì)的垃圾收集器的日志也可以看到。和所有的跟蹤技術(shù)一樣,日值記錄詳細(xì)的程度影響著JVM的性能,你想得到的信息越詳細(xì),付出的代價(jià)也就越高。為了能夠判斷內(nèi)存是否泄漏,我使用了能夠顯示輩分之間所有的不同的較權(quán)威的技術(shù)來(lái)顯示他們的區(qū)別,并以此來(lái)得到結(jié)果。SUN 的日志報(bào)告提供的信息比這個(gè)詳細(xì)的程度超過(guò)5%,我的很多客戶(hù)都一直使用那些設(shè)置來(lái)保證他們管理和調(diào)整垃圾收集器。下面的這個(gè)設(shè)置能夠給你提供足夠的分析數(shù)據(jù):
    –verbose:gc –xloggc:gc.log –XX:+PrintGCDetails –XX:+PrintGCTimeStamps

    明確發(fā)現(xiàn)在整個(gè)堆中存在有潛在可能泄漏內(nèi)存的情況,用老一代增長(zhǎng)的速率才比較有說(shuō)服力。切記調(diào)查不能決定些什么:為了能夠最終確定你內(nèi)存泄漏,你需要離線(xiàn)在內(nèi)存模擬器中運(yùn)行你的應(yīng)用程序。

    IBM JVM內(nèi)存管理模式
    IBMJVM的機(jī)制有一點(diǎn)不同。它不是運(yùn)行在一個(gè)巨大的繼承HEAP中,它僅在一個(gè)單一的地區(qū)維護(hù)了所有的對(duì)象同時(shí)隨著堆的增長(zhǎng)來(lái)釋放內(nèi)存。這個(gè)堆是這樣運(yùn)行的:在一開(kāi)始運(yùn)行的時(shí)候,它會(huì)很小,隨著對(duì)象實(shí)例不斷的填充,在需要執(zhí)行垃圾收集的地方清除掉無(wú)效的對(duì)象同時(shí)把所有有效的對(duì)象緊湊的放置到堆的底部。因此你可能猜測(cè)到了,如果想尋找可能發(fā)生的內(nèi)存泄漏應(yīng)該觀(guān)察堆中所有的動(dòng)作,堆的使用率是在提高?

    如何分析內(nèi)存泄漏
    內(nèi)存泄漏非常難確定,如果你能夠確定是請(qǐng)求導(dǎo)致的,那你的工作就非常簡(jiǎn)單了。把你的程序放入到運(yùn)行環(huán)境中,并在內(nèi)存模擬器中運(yùn)行,按下面的步驟來(lái):
    1        在內(nèi)存模擬器中運(yùn)行你的應(yīng)用程序
    2        執(zhí)行使用方案(制造請(qǐng)求)以便讓程序在內(nèi)存中裝載請(qǐng)求所需要的所有的對(duì)象,這可以為以后詳細(xì)的分析排除不必要的干擾
    3        在執(zhí)行使用方案前對(duì)堆進(jìn)行拍照以便捕獲其中所有運(yùn)行的對(duì)象。
    4        再次運(yùn)行使用方案。
    5        再次拍照,來(lái)捕獲使用方案運(yùn)行之后堆中所有對(duì)象的狀態(tài)。
    6        比較這2個(gè)快照,找出執(zhí)行使用方案后本不應(yīng)該出現(xiàn)在堆中的對(duì)象。

    這個(gè)時(shí)候,你需要去和開(kāi)發(fā)者交流,告訴他你所碰到的棘手的請(qǐng)求,他們可以判斷究竟是對(duì)象泄漏還是為了某個(gè)目的特地讓對(duì)象保留下來(lái)的。如果執(zhí)行完后并沒(méi)有發(fā)現(xiàn)內(nèi)存泄漏的情況,我一般會(huì)轉(zhuǎn)到步驟4再進(jìn)行多次類(lèi)似的跟蹤。比如,我可能會(huì)將我的請(qǐng)求反復(fù)運(yùn)行17次,希望我的泄漏分析能夠得到17個(gè)情況(或更多)。這個(gè)方法不一定總有用,但如果是因?yàn)檎?qǐng)求引起的對(duì)象泄漏的話(huà),就會(huì)有很大的幫助。

    如果你無(wú)法明確的判斷泄漏是因?yàn)檎?qǐng)求引發(fā)的,你有2個(gè)選擇:
    1        模擬每一個(gè)被懷疑的請(qǐng)求直至發(fā)現(xiàn)內(nèi)存泄漏
    2        存配置一個(gè)內(nèi)存性能跟蹤工具

    第一個(gè)選項(xiàng)在小應(yīng)用程序中是確實(shí)可用的或者你非常走運(yùn)的解決了問(wèn)題,但對(duì)大型應(yīng)用程序不太有用。如果你有跟蹤工具的話(huà)第二個(gè)選擇是比較有用的。這些工具利用字節(jié)流工具跟蹤對(duì)象的創(chuàng)建和銷(xiāo)毀的數(shù)量,他們可以報(bào)告特定類(lèi)中的對(duì)象的數(shù)量狀態(tài),例如把Collections類(lèi)作為特定的請(qǐng)求。例如,一個(gè)跟蹤工具可以跟蹤/action/login.do請(qǐng)求,并在它完成后將其中的100個(gè)對(duì)象放入HASHMAP中。這個(gè)報(bào)告并不能告訴你造成泄漏的是代碼還是某個(gè)對(duì)象,而是告訴你在內(nèi)存模擬器中應(yīng)該留意那些類(lèi)型的請(qǐng)求。把程序服務(wù)器放到產(chǎn)品環(huán)境中并不會(huì)使他們變敏感,而是跟蹤性能的工具可以使你的工作變的更簡(jiǎn)單化。

    虛假內(nèi)存泄漏
    少數(shù)的一些問(wèn)題看起來(lái)是內(nèi)存泄漏實(shí)際上并非如此。
    我將這些情況稱(chēng)為假泄漏,表現(xiàn)在下面幾種情況:
    1        分析過(guò)早
    2        Session泄漏
    3        異常的持久區(qū)域

    這章節(jié)對(duì)這些假泄漏都進(jìn)行了調(diào)查,描述了如何去判斷這些情況以及如何處理.

    不要過(guò)早分析
    為了在尋找內(nèi)存泄漏的時(shí)候盡量減少出現(xiàn)判斷錯(cuò)誤的可能性,你應(yīng)當(dāng)在適當(dāng)?shù)臅r(shí)候分析堆。危險(xiǎn)是:一些生命周期長(zhǎng)的對(duì)象需要裝載到堆中,因此在堆達(dá)到穩(wěn)定狀態(tài)且包含了核心對(duì)象之前具有很大的欺騙性。在分析堆之前,應(yīng)該讓?xiě)?yīng)用程序達(dá)到穩(wěn)定狀態(tài)。
    為了判斷是否過(guò)早的對(duì)堆進(jìn)行分析,持續(xù)2個(gè)小時(shí)對(duì)跟蹤到的分析快照進(jìn)行分析,看堆的使用率是上升還是下降。如果是下降,保存這個(gè)時(shí)候的內(nèi)存記錄。如果是上升,這個(gè)時(shí)候就需要分析內(nèi)存中的SESSION了。

    發(fā)生泄漏的session
    WEB請(qǐng)求經(jīng)常導(dǎo)致內(nèi)存泄漏,在一個(gè)WEB請(qǐng)求中,對(duì)象會(huì)被限制存儲(chǔ)在有限的幾個(gè)區(qū)域。這些區(qū)域就是:
    1        頁(yè)面區(qū)域
    2        請(qǐng)求區(qū)域
    3        上下文區(qū)域
    4        應(yīng)用程序區(qū)域
    5        靜態(tài)變量
    6        長(zhǎng)生命周期的變量,例如SERVLET

    當(dāng)實(shí)現(xiàn)一些JSPJAVASERVER頁(yè)面)時(shí),在頁(yè)面上聲明的變量在頁(yè)面結(jié)束的時(shí)候就被釋放,這些變量?jī)H僅在這個(gè)單獨(dú)的頁(yè)面存在時(shí)存在。WEB服務(wù)器會(huì)向應(yīng)用程序服務(wù)器傳送一系列參數(shù)和屬性,也就是在SERVLETJSP之間傳輸HttpServletRequest中的對(duì)象。你的動(dòng)態(tài)頁(yè)面依靠HttpServletRequest在不同的組件之間傳輸信息,但當(dāng)請(qǐng)求完成或者socket結(jié)束的時(shí)候,SERVLET控制器會(huì)釋放所有在HttpServletRequest 中的對(duì)象。這些對(duì)象僅在他們的請(qǐng)求的生命周期內(nèi)存在。

    HTTP是無(wú)狀態(tài)的,這意味著客戶(hù)向服務(wù)器發(fā)送一個(gè)請(qǐng)求,服務(wù)器回應(yīng)這個(gè)請(qǐng)求,這個(gè)傳遞就完成了,就是會(huì)話(huà)結(jié)束了。我們應(yīng)該感激WEB頁(yè)面幫我們做的日志,這樣我們就能向購(gòu)物車(chē)放置東西,并去檢查它,服務(wù)器能夠定義一個(gè)跨越多請(qǐng)求的擴(kuò)展對(duì)話(huà)。屬性和參數(shù)被放在各自用戶(hù)的HttpSession對(duì)象中,并通過(guò)它讓程序的SERVLETJSP交流。利用這種辦法,頁(yè)面存儲(chǔ)你的信息并把他們添加到HttpSession中,因此你可以用購(gòu)物車(chē)購(gòu)買(mǎi)東西,并檢查商品和使用信用卡付帳。作為一個(gè)無(wú)狀態(tài)的協(xié)議,它總是客戶(hù)端發(fā)起連接請(qǐng)求,服務(wù)器需要知道一個(gè)會(huì)話(huà)存在多長(zhǎng)時(shí)間,到時(shí)候就應(yīng)該釋放這個(gè)用戶(hù)的數(shù)據(jù)。超過(guò)這個(gè)會(huì)話(huà)的最長(zhǎng)時(shí)間就是會(huì)話(huà)超時(shí),他們?cè)诔绦蚍?wù)器中設(shè)置。除非明確的要求釋放對(duì)象或者這個(gè)會(huì)話(huà)失效,否則在會(huì)話(huà)超時(shí)之前會(huì)話(huà)中的對(duì)象會(huì)一直存在。

    正如session是為每個(gè)用戶(hù)管理對(duì)象一樣,ServletContext為整個(gè)程序管理對(duì)象。ServletContext的有效范圍是整個(gè)程序,因此你可以利用Servlet中的ServletContext或者JSP應(yīng)用程序?qū)ο笤谒械?/span>ServletJSP之間讓在這個(gè)程序中的所有用戶(hù)共享數(shù)據(jù)。ServletContext是最主要的存放程序配置信息和緩存程序數(shù)據(jù)的地方,例如JNDI的信息。

    如果數(shù)據(jù)不是存儲(chǔ)這個(gè)四個(gè)地方(頁(yè)面范圍,請(qǐng)求范圍,會(huì)話(huà)范圍,程序范圍)那就可能存儲(chǔ)在下面的對(duì)象中:
    1        靜態(tài)變量
    2        長(zhǎng)生命周期的類(lèi)變量

    每個(gè)類(lèi)的靜態(tài)變量被JVMJAVA虛擬機(jī))所控制,他們存在與否和類(lèi)是否已經(jīng)被初始化無(wú)關(guān)。一個(gè)類(lèi)的所有實(shí)例共用一個(gè)存儲(chǔ)靜態(tài)變量的地方,因此在任何一個(gè)實(shí)例中修改靜態(tài)變量會(huì)影響這個(gè)類(lèi)的其他實(shí)例。因此,如果一個(gè)程序在靜態(tài)變量中存放了一個(gè)對(duì)象,如果這個(gè)變量生命周期沒(méi)有到,那么這個(gè)對(duì)象就不會(huì)被JVM釋放。這些靜態(tài)對(duì)象是造成內(nèi)存泄漏的主要原因。

    最后,對(duì)象能夠被放到內(nèi)部數(shù)據(jù)類(lèi)型或者長(zhǎng)生命周期類(lèi)中的成員變量中,例如SERVLET。當(dāng)一個(gè)SERVLET被創(chuàng)建并且被裝載到內(nèi)存,它在內(nèi)存中僅有一個(gè)實(shí)例,采用多線(xiàn)程去訪(fǎng)問(wèn)這個(gè)SERVLET實(shí)例。如果在INIT()方法中裝載配置信息,將他存儲(chǔ)于類(lèi)變量中,那么當(dāng)需要維護(hù)的時(shí)候就可以隨時(shí)讀出這些信息,這樣所有的對(duì)象就用相同的配置。我常碰到的一個(gè)問(wèn)題就是利用SERVLET類(lèi)變量去存儲(chǔ)象頁(yè)面緩存這樣的信息。在他們自己內(nèi)部本身存貯這些緩存配置是個(gè)不錯(cuò)的選擇,但存貯在SERVLET中是最糟糕的情況。如果你需要使用緩存,你最好使用第三方控制插件,例如 TANGOSOLCOHERENCE。

    當(dāng)在頁(yè)面或者請(qǐng)求范圍中利用變量存放對(duì)象的時(shí)候,在他們結(jié)束的時(shí)候這些對(duì)象會(huì)自動(dòng)釋放。同樣,在SESSION中存放對(duì)象的時(shí)候,當(dāng)程序明確說(shuō)明此SESSION失效的或者會(huì)話(huà)執(zhí)行超時(shí)的時(shí)候,這些對(duì)象才會(huì)自動(dòng)被釋放。

    很多看起來(lái)象內(nèi)存泄漏的情況都是上面的那些會(huì)話(huà)中的泄漏。一個(gè)造成泄漏的會(huì)話(huà)并不是泄漏了內(nèi)存而是類(lèi)似于泄漏,它消耗了內(nèi)存,但最終這些內(nèi)存都會(huì)被釋放的。如果程序服務(wù)器發(fā)生內(nèi)存溢出,判斷是內(nèi)存泄漏還是內(nèi)存缺乏的最好的方法就是:停止所有向這個(gè)服務(wù)器所發(fā)的請(qǐng)求的對(duì)象,等待會(huì)話(huà)超時(shí),看內(nèi)存時(shí)候會(huì)被釋放出來(lái)。這雖然不會(huì)一定能夠達(dá)到你要的目的,但是這是最好的分段處理方法,當(dāng)你裝載測(cè)試器的時(shí)候,你應(yīng)該先掛斷你內(nèi)容巨大的會(huì)話(huà)而不是先去尋找內(nèi)存泄漏。

    通常來(lái)說(shuō),如果你執(zhí)行了一個(gè)很大的會(huì)話(huà),你應(yīng)該盡量去減少它所占用的內(nèi)存空間,如果可以的話(huà)最好能重構(gòu)程序,以減少session所占據(jù)的內(nèi)存空間。下面2種方法可以降低大會(huì)話(huà)和內(nèi)存的沖突:
    1        增大堆的空間以支持你的大會(huì)話(huà)
    2        縮短會(huì)話(huà)的超時(shí)時(shí)間,讓它能夠快速的失效

    一個(gè)巨大的堆會(huì)導(dǎo)致垃圾回收花費(fèi)更多的時(shí)間,因此這不是一個(gè)好解決方法,但總比發(fā)生OutofMemoryError強(qiáng)。增加足夠的堆空間以使它能夠存儲(chǔ)所有應(yīng)該保存的有效值,也意味著你必須有足夠的內(nèi)存去存儲(chǔ)所有訪(fǎng)問(wèn)你站點(diǎn)的用戶(hù)的有效會(huì)話(huà)。如果商業(yè)規(guī)則允許的話(huà)最好能縮短會(huì)話(huà)超時(shí)的時(shí)間,以減少堆占用空間的沖突。
    總結(jié)下,你應(yīng)該依據(jù)合理性和重要性按下面的步驟依次去執(zhí)行:
    1        重構(gòu)程序,盡量減少擁有session范圍的變量所存儲(chǔ)的信息量
    2        鼓勵(lì)你的客戶(hù)在他們使用完后,明確的釋放會(huì)話(huà)
    3        縮短超時(shí)的時(shí)間,以便于讓你內(nèi)存盡快的得到回收
    4        增加你堆空間的大小

    無(wú)論如何,不要讓程序范圍級(jí)的變量,靜態(tài)變量,長(zhǎng)生命周期的類(lèi)存儲(chǔ)對(duì)象,事實(shí)上,你需要在內(nèi)存模擬器中去分析泄漏。
    異常的持久空間

    容易誤解JVM為持久空間分配內(nèi)存的目的。堆僅僅存儲(chǔ)類(lèi)的實(shí)例,但JVM在堆中創(chuàng)建類(lèi)實(shí)例之前,它必須把字節(jié)流文件(.class文件)裝載到程序內(nèi)存中。它利用內(nèi)存中的字節(jié)流在堆中創(chuàng)建類(lèi)的實(shí)例。JVM利用程序的內(nèi)存來(lái)裝載字節(jié)流文件,這個(gè)內(nèi)存空間稱(chēng)為持久空間。圖6顯示了持久空間和堆的關(guān)系:它存在于JVM程序中,并不是堆的一部分。


    Figure 6. The relationship between the permanent space and the heap

    通常,你可能想讓你的持久空間足夠大以便于它能夠裝載你程序所有的類(lèi),因?yàn)楹苊黠@,從文件系統(tǒng)中讀取類(lèi)文件比從內(nèi)存中裝載代價(jià)高很多。JVM提供了一個(gè)參數(shù)讓你不的程序不卸載已經(jīng)裝載到持久空間中的類(lèi)文件:
    –noclassgc

    這個(gè)參數(shù)選項(xiàng)告訴JVM不要跑到持久空間去執(zhí)行垃圾收集釋放其中已經(jīng)裝載的類(lèi)文件。這個(gè)參數(shù)選項(xiàng)很聰明,但是會(huì)引起一個(gè)問(wèn)題:當(dāng)持久空間滿(mǎn)了以后依然需要裝載新文件的時(shí)候JVM會(huì)怎么處理呢?我觀(guān)測(cè)到的資料說(shuō)明:如果JVM檢測(cè)到持久空間還需要內(nèi)存,就會(huì)調(diào)用主垃圾收集程序。垃圾收集器清除堆,但它并不會(huì)對(duì)持久空間進(jìn)行任何操作,因此它的努力是白費(fèi)的。于是JVM就再重新檢測(cè)持久空間,看它是否滿(mǎn),然后再次執(zhí)行程序,一遍的一遍重復(fù)。

    我第一次碰到這種問(wèn)題的時(shí)候,用戶(hù)抱怨說(shuō)程序性能很差勁,并且在運(yùn)行了幾次后就出現(xiàn)了問(wèn)題,可能是內(nèi)存溢出問(wèn)題。在我調(diào)查了詳細(xì)的關(guān)于堆和程序內(nèi)存利用的收集器的記錄后,我迅速發(fā)覺(jué)堆的狀態(tài)非常正常,但程序確發(fā)生了內(nèi)存溢出。這個(gè)用戶(hù)維持了數(shù)千的JSP頁(yè)面,在裝載到內(nèi)存前把他們都編譯成了字節(jié)流文件放入持久空間。他的環(huán)境已經(jīng)造成了持久空間溢出,但是在堆中由于用了 noclassgc 選項(xiàng),于是JVM并不去釋放類(lèi)文件來(lái)裝載新的類(lèi)文件。于是就導(dǎo)致了內(nèi)存溢出錯(cuò)誤,我把他的持久空間改為512M大小,并去掉了 noclassgc 參數(shù)。

    正像圖7顯示的,當(dāng)持久空間變滿(mǎn)了的時(shí)候,就引發(fā)垃圾收集,清理了樂(lè)園和幸存者空間,但是并不釋放持久空間中的一點(diǎn)內(nèi)存。


    Figure 7. Garbage collection behavior when the permanent space becomes full. Click on thumbnail to view full-sized image.          
                            
    注意
    當(dāng)設(shè)置持久空間大小時(shí)候,一般考慮128M,除非你的程序有很多的類(lèi)文件,這個(gè)時(shí)候,你就可以考慮使用256M大小。如果你想讓他能夠裝載所有的類(lèi)的時(shí)候,就會(huì)導(dǎo)致一個(gè)典型的結(jié)構(gòu)錯(cuò)誤。設(shè)置成512M就足夠了,它僅僅是暫時(shí)的時(shí)間的花費(fèi)。把持久空間設(shè)置成512M大小就象給一個(gè)腳痛的人吃止痛藥,雖然暫時(shí)緩解了痛,但是腳還是沒(méi)有好,依然需要醫(yī)生把痛治療好,否則只是把問(wèn)題延遲了而已。

    線(xiàn)程池

    外界同WEB或程序服務(wù)器連接的主要方法就是向他們發(fā)送請(qǐng)求,這些請(qǐng)求被放置到程序的執(zhí)行次序隊(duì)列中。和內(nèi)存最大的沖突就是程序服務(wù)器所設(shè)置的線(xiàn)程池的大小。線(xiàn)程池的大小就是程序可以同時(shí)處理的請(qǐng)求的數(shù)量。如果池太小,請(qǐng)求就需要在隊(duì)列中等待程序處理,如果太大,CPU就需要花費(fèi)太多的時(shí)間在這些眾多的線(xiàn)程之間來(lái)回的切換。
    每個(gè)服務(wù)器都有一個(gè)SOCKET負(fù)責(zé)監(jiān)聽(tīng)。程序把接受到的請(qǐng)求放到待執(zhí)行隊(duì)列中,然后將這個(gè)請(qǐng)求從隊(duì)列移動(dòng)到線(xiàn)程中被程序處理。

    8顯示了服務(wù)器的處理程序。


    Figure 8. 服務(wù)器處理請(qǐng)求的次序結(jié)構(gòu)

    線(xiàn)程池太小
    每當(dāng)我碰到有人抱怨裝載速度的性能隨著裝載的數(shù)量的增加變的越來(lái)越糟糕的時(shí)候,我會(huì)首先檢查線(xiàn)程池。特別是,我在看到下面這些信息的時(shí)候:
    1.線(xiàn)程池的使用
    2.很多請(qǐng)求等待處理(在隊(duì)列中等待處理)

    當(dāng)一個(gè)線(xiàn)程池被待處理的請(qǐng)求裝滿(mǎn)的時(shí)候,響應(yīng)的時(shí)間就變的極其糟糕,因?yàn)檫@些在隊(duì)列中等待處理的請(qǐng)求會(huì)消耗很多的額外時(shí)間。這個(gè)時(shí)候,CPU的利用率會(huì)非常低,因?yàn)槌绦蚍?wù)器沒(méi)有時(shí)間去指揮CPU工作。這個(gè)時(shí)候,我會(huì)按一定幅度增加調(diào)節(jié)池的大小,并在未處理請(qǐng)求的數(shù)量減少前一直監(jiān)視程序的吞吐量,你需要一個(gè)合理甚至更好的負(fù)載量者,一個(gè)精確的負(fù)載量測(cè)試工具可以準(zhǔn)確的幫你測(cè)試出結(jié)果。當(dāng)你觀(guān)測(cè)吞吐量的時(shí)候,如果你發(fā)現(xiàn)吞吐量降低了,你就應(yīng)該把池的大小下調(diào)一個(gè)幅度,一直到找到讓它保持最大吞吐量的大小為止。

    9顯示了連接池太小的情況


    Figure 9. 所有的線(xiàn)程都被占用了,請(qǐng)求就只能在隊(duì)列中等待

    每當(dāng)我閱讀性能調(diào)整手冊(cè)的時(shí)候,最讓我頭疼的就是他們從來(lái)不告訴你特殊情況下線(xiàn)程池應(yīng)該是多大。由于這些值非常依賴(lài)程序的行為,他們只告訴你大普通情況下正確的大小,但是他們給了你一個(gè)范圍內(nèi)的值,這對(duì)用戶(hù)很有利的。例如考慮下面2種情況:
    1        一個(gè)程序從內(nèi)存中讀出一個(gè)字符串,把它傳給JSP頁(yè)面,讓JSP頁(yè)面去顯示
    2        另一個(gè)程序從數(shù)據(jù)庫(kù)中讀出1000個(gè)數(shù)值,為這些不規(guī)則的數(shù)值求平均。第一個(gè)程序?qū)φ?qǐng)求的回應(yīng)會(huì)很塊,大概僅需要不足0.25秒的時(shí)間,且不怎么占據(jù)CPU。第二個(gè)程序可能需要3秒去回應(yīng),同時(shí)會(huì)占據(jù)CPU。因此,為第一個(gè)程序配置的池大小是100就有點(diǎn)太小了,因?yàn)槌绦蚰軌蛲瑫r(shí)處理200個(gè);但為第二個(gè)程序配置的池是100,就有點(diǎn)太大了,因?yàn)?/span>CPU可能就能應(yīng)付50個(gè)線(xiàn)程。
    但是,很多程序并沒(méi)有在這種情況下動(dòng)態(tài)的去調(diào)整的功能。多數(shù)情況下是做相同的事,但是應(yīng)該為他們劃分范圍。因此,我建議你為一個(gè)CPU分配5075個(gè)左右的線(xiàn)程。對(duì)一些程序來(lái)說(shuō),這個(gè)數(shù)量可能太少,對(duì)另一個(gè)些來(lái)說(shuō)可能太多,我剛開(kāi)始為每個(gè)CPU分配5075個(gè)線(xiàn)程,然后根據(jù)吞吐量和CPU的性能,并做適當(dāng)?shù)恼{(diào)整。

    線(xiàn)程池太大
    除了線(xiàn)程池?cái)?shù)量太小之外的情況外,環(huán)境也可能把線(xiàn)程數(shù)量配置的過(guò)大。當(dāng)這些環(huán)境中的負(fù)載量不斷增大的時(shí)候,CPU的使用率會(huì)持續(xù)無(wú)法降低,就沒(méi)有什么響應(yīng)請(qǐng)求的時(shí)間了,因?yàn)?/span>CPU只顧的在眾多的線(xiàn)程之間來(lái)回的切換跳動(dòng),沒(méi)時(shí)間讓線(xiàn)程去做他們應(yīng)該做的事了。

    連接池過(guò)大的最主要的跡象就是CPU的使用率一直很高。有些時(shí)候,垃圾收集也可能導(dǎo)致CPU使用率很高,但是垃圾收集導(dǎo)致的CPU使用率很高和池過(guò)大導(dǎo)致的使用率有一個(gè)主要的區(qū)別就是:垃圾收集引起的只是短時(shí)間的高使用率就象個(gè)釘子,而池過(guò)大導(dǎo)致的就是一直持續(xù)很高呈線(xiàn)性。

    這個(gè)情況發(fā)生的時(shí)候,請(qǐng)求會(huì)被放在隊(duì)列中不被處理,但是不會(huì)始終如此,因?yàn)檎?qǐng)求占用CPU的情況和程序占用的情況造成的后果不同。降低線(xiàn)程池的大小可能會(huì)讓請(qǐng)求等待,但是讓請(qǐng)求等待總比為了處理請(qǐng)求而讓CPU忙不過(guò)來(lái)的好。讓CPU保持持續(xù)的高使用率,同時(shí)性能不降低,新請(qǐng)求到來(lái)的時(shí)候放入到隊(duì)列中,這是最理想的程序。考慮下面這個(gè)很類(lèi)似的情況:很多高速公里有交通燈來(lái)保證車(chē)輛進(jìn)入到擁擠的公里中。在我看來(lái),這些交通燈根本沒(méi)用,道理很充分。比如你來(lái)了,在交通燈后面的安全線(xiàn)上等待進(jìn)入到高速公路上。如果所有的車(chē)輛都同時(shí)涌向公里,我們就動(dòng)彈不得,但是只要減緩涌向高速公路車(chē)輛的速度,交通遲早會(huì)暢通。事實(shí)上,很多的大城市都有這樣功能,但根本沒(méi)用,他們真正需要的是一些更多的小路(CPU),涌向高速公路的速度真的降低了,那么交通會(huì)變的正常起來(lái)。

    設(shè)置一個(gè)飽和的池,然后逐步減少連接池大小,一直到CPU占用率為75%到85%之間,同時(shí)用戶(hù)負(fù)載正常。如果等待隊(duì)列大小實(shí)在無(wú)法控制,考慮下面2中建議:
    1.把你的程序放入代碼模擬器運(yùn)行,調(diào)整程序代碼
    2.增加額外的硬件

    如果你的用戶(hù)負(fù)載超過(guò)了環(huán)境能承受的范圍,你應(yīng)該考慮修正代碼減少和CPU的沖突或者增加CPU。

    JDBC連接池

    很多JAVA EE 程序連接到一個(gè)后臺(tái)數(shù)據(jù)源,大多數(shù)是通過(guò)JDBCJAVA DATABASE CONNECTIVITY)將程序和后臺(tái)連接起來(lái)。由于創(chuàng)建數(shù)據(jù)庫(kù)連接的代價(jià)很高,程序服務(wù)器讓在同一個(gè)程序服務(wù)器實(shí)例下的所有程序共享特定數(shù)量的一些連接。如果一個(gè)請(qǐng)求需要連接到數(shù)據(jù)庫(kù),但是數(shù)據(jù)庫(kù)的連接池?zé)o法為這個(gè)請(qǐng)求創(chuàng)建一個(gè)新連接,這個(gè)時(shí)候請(qǐng)求就會(huì)停下來(lái)等待連接池完成自己的操作再給她分配一個(gè)連接。反過(guò)來(lái),如果數(shù)據(jù)庫(kù)連接池太大程序服務(wù)器就會(huì)浪費(fèi)資源,并且程序有可能強(qiáng)迫數(shù)據(jù)庫(kù)承受過(guò)量的負(fù)荷。我們調(diào)試的目的就是盡量減少請(qǐng)求的等待時(shí)間和飽和的資源之間之間的沖突,讓一個(gè)請(qǐng)求在數(shù)據(jù)庫(kù)外等待要比強(qiáng)迫數(shù)據(jù)庫(kù)好的多。

    一個(gè)程序服務(wù)器如果設(shè)置連接的數(shù)量不合理就會(huì)有下面這些特征:
    1.程序運(yùn)行速度緩慢
    2CPU使用率低
    3.?dāng)?shù)據(jù)庫(kù)連接池使用率非常高
    4.線(xiàn)程等待數(shù)據(jù)庫(kù)的連接
    5.線(xiàn)程使用率很高
    6.請(qǐng)求隊(duì)列中有待處理的請(qǐng)求(潛在的)
    7.?dāng)?shù)據(jù)庫(kù)CPU使用率很低(因?yàn)闆](méi)有足夠的請(qǐng)求能夠讓他繁忙起來(lái))

    JDBC prepared statements
    JDBC相關(guān)的另一個(gè)重要的設(shè)置就是:為JDBC使用的statement 所預(yù)設(shè)的緩存的大小。當(dāng)你的程序在數(shù)據(jù)庫(kù)中運(yùn)行SQL statement 的時(shí)候三下面3個(gè)步驟進(jìn)行:
    1.準(zhǔn)備
    2.執(zhí)行
    3.返回?cái)?shù)值

    在準(zhǔn)備階段,數(shù)據(jù)庫(kù)驅(qū)動(dòng)器讓數(shù)據(jù)庫(kù)完成隊(duì)列中的執(zhí)行計(jì)劃。執(zhí)行的時(shí)候,數(shù)據(jù)庫(kù)執(zhí)行語(yǔ)句并返回指向結(jié)果的引用。在返回的時(shí)候,程序重新描述這些結(jié)果并描述出這些被請(qǐng)求的信息。

    數(shù)據(jù)庫(kù)驅(qū)動(dòng)會(huì)這樣優(yōu)化程序:首先,你需要去準(zhǔn)備一個(gè)statement ,這個(gè)statement 它會(huì)讓數(shù)據(jù)庫(kù)做好執(zhí)行和緩存結(jié)果的準(zhǔn)備。在此同時(shí),數(shù)據(jù)庫(kù)驅(qū)動(dòng)會(huì)從緩存中裝載已經(jīng)準(zhǔn)備好的statement ,而不用直接連接到數(shù)據(jù)庫(kù)。

    如果prepared statement 設(shè)置太小,數(shù)據(jù)庫(kù)驅(qū)動(dòng)器會(huì)被迫去查詢(xún)沒(méi)有裝載進(jìn)緩存區(qū)的statement ,這就會(huì)增加額外的連接到數(shù)據(jù)庫(kù)的時(shí)間。prepared statement 緩存區(qū)設(shè)置不恰當(dāng)最主要的癥狀就是花費(fèi)大量的時(shí)間去連接相同的statement。這段被浪費(fèi)的時(shí)間本來(lái)是為了讓它去裝載后面的調(diào)用的。

    事情變的稍微復(fù)雜了點(diǎn),緩存prepared statement 是每個(gè)statement的基礎(chǔ),就是說(shuō)在一個(gè)statement連接之前都應(yīng)當(dāng)緩存起來(lái)。這個(gè)增加的復(fù)雜性就產(chǎn)生了一個(gè)沖突:如果你有100個(gè)prepared statement需要去緩存,但你的連接池中有50個(gè)數(shù)據(jù)庫(kù)連接,這個(gè)時(shí)候你就需要有存放5000條預(yù)備語(yǔ)句的內(nèi)存。

    通過(guò)跟蹤性能,確定出你程序所執(zhí)行的不重復(fù)的statement 的數(shù)量,并從這些statement 中找出哪些條是頻繁執(zhí)行的。

    Entity bean(實(shí)體BEAN)和stateful session bean的緩沖

    無(wú)狀態(tài)(stateless)對(duì)象可以被放入到池中共享,但象Entity beans stateful session bean這樣的有狀態(tài)的對(duì)象就需要被緩存,因?yàn)檫@些bean的每個(gè)實(shí)例都是不相同的。當(dāng)你需要一個(gè)有狀態(tài)對(duì)象時(shí),你需要明確創(chuàng)建這個(gè)對(duì)象的特定實(shí)例,普通的實(shí)例是不能滿(mǎn)足的。類(lèi)似的,你考慮一個(gè)超市類(lèi)似的情況,你需要個(gè)售貨員但他叫什么并不重要,任何售貨員都可以滿(mǎn)足你。也就是,售貨員被放入池中共享,因?yàn)槟阒恍枰鞘圬泦T就可以,而不是一個(gè)叫做史締夫的這個(gè)售貨員。當(dāng)你離開(kāi)超市的時(shí)候,你需要帶上你的孩子,不是其他人的孩子,而是你自己的。這個(gè)時(shí)候,孩子就需要被緩存。


    Figure 10. The application requests an object from the cache that is in the cache, so a reference to that object is returned without making a network trip to the database

    當(dāng)你的緩存區(qū)太小的時(shí)候,緩存的性能就會(huì)明顯的受到影響。特別是,當(dāng)一個(gè)請(qǐng)求去一個(gè)已經(jīng)滿(mǎn)了的緩存區(qū)域去請(qǐng)求一個(gè)對(duì)象的時(shí)候,下面的步驟就會(huì)執(zhí)行,這些步驟會(huì)在圖11中顯示:
    1        程序請(qǐng)求一個(gè)對(duì)象
    2        緩存檢測(cè)這個(gè)對(duì)象是否已經(jīng)存在于緩存中
    3        緩存決定把一個(gè)對(duì)象開(kāi)除出緩存(一般采用的算法是遺棄最近使用次數(shù)最少的對(duì)象)
    4        把這個(gè)對(duì)象扔出緩存(稱(chēng)為passivated
    5        把從數(shù)據(jù)庫(kù)中裝載這個(gè)新對(duì)象并放入到緩存(稱(chēng)為activated
    6        把指向這個(gè)對(duì)象的引用返回給程序


    Figure 11. Because the requested object is not in the cache, an object must be selected for removal from the cache and removed from it.

    如果多數(shù)的請(qǐng)求都需要執(zhí)行這些步驟的話(huà),那你采用緩存技術(shù)就不是好的選擇了!如果這些處理步驟頻繁發(fā)生的話(huà),你就需要重新推敲下你的緩存了?;貞浺幌拢簭木彺嬷腥コ粋€(gè)對(duì)象稱(chēng)為passivation,從持久存儲(chǔ)區(qū)取出一個(gè)對(duì)象放入緩存稱(chēng)為activation。能在緩存中找到的請(qǐng)求(緩存中有此請(qǐng)求的對(duì)象)的百分率稱(chēng)為hit ratio,相反找不到的請(qǐng)求的百分率稱(chēng)為miss ratio。

    緩存剛被初始化的時(shí)候,hit ratio0,它的activation數(shù)量非常高,因此在初始化后你需要去觀(guān)察緩存的性能。初始化以后,你應(yīng)該跟蹤passivation的數(shù)量并把它和與向緩存請(qǐng)求對(duì)象的請(qǐng)求的總量相比較,因?yàn)?/span>passivations只會(huì)發(fā)生在緩存被初始化以后。但一般來(lái)說(shuō),我們更需要關(guān)心緩存的miss ratio。如果miss ratio超過(guò)25%,那么緩存可能是太小了。因此,如果missratio的數(shù)量超過(guò)75%,那么不是你的緩存設(shè)置的太小就是你不需要緩存這個(gè)技術(shù)。

    一旦你覺(jué)得你的緩存太小,就去嘗試著增大大小,并測(cè)試增加的性能。如果miss ration下降到20%以下,那你的緩存的大小就非常棒了,如果沒(méi)有什么效果,那么你就需要和這個(gè)程序的技術(shù)員聯(lián)系,看是這個(gè)對(duì)象是不是需要緩存或者是否應(yīng)該修正程序中這個(gè)對(duì)象的代碼。
    Staless session beanmessage-driven bean
    Stateless session bean message-driven bean 在商業(yè)應(yīng)用方面很重要,不要期望它們會(huì)保持自己特有的狀態(tài)信息。當(dāng)你的程序需要使用這些BEAN的商業(yè)功能的時(shí)候,它就從一個(gè)池中取出一個(gè)BEAN實(shí)例,用這個(gè)實(shí)例來(lái)調(diào)用一個(gè)個(gè)方法,用完后再將BEAN的實(shí)例再放回到池中。如果你的程序過(guò)了一會(huì)又需要這個(gè)一摸一樣的BEAN,就從池中再得到一個(gè)實(shí)例,但不能保證你得到的就是上一個(gè)實(shí)例。池能夠讓程序共享資源,但是會(huì)讓你的程序付出潛在的等待時(shí)間。如果你無(wú)法從池中得到想要的BEAN,請(qǐng)求就會(huì)等待,一直到這個(gè)BEAN被放入到池中。很多程序服務(wù)器都會(huì)把這些池調(diào)整的很好,但是我碰到過(guò)因?yàn)樵诃h(huán)境中把他們?cè)O(shè)置的太小而引發(fā)的不少麻煩。Stateless bean池的大小應(yīng)該和可執(zhí)行線(xiàn)程池的大小一般大,因?yàn)橐粋€(gè)線(xiàn)程同時(shí)只能使用一個(gè)對(duì)象,再多了就造成浪費(fèi)的。因此,一些程序服務(wù)器把池的大小和線(xiàn)程的數(shù)量設(shè)置成同樣的數(shù)量。為了保險(xiǎn)起見(jiàn),你應(yīng)該親自把它設(shè)置成這個(gè)數(shù)。
    事務(wù)
    使用Enterprise Java的一個(gè)好處就是它天生就支持事務(wù)。通過(guò)JAVAEE 5 EJBEnterprise javaBeans)的注釋?zhuān)憧梢钥刂剖聞?wù)中方法的使用。事務(wù)會(huì)以下面2中方式結(jié)束:
    1        事務(wù)提交
    2        事務(wù)回滾
    當(dāng)一個(gè)事務(wù)被提交的時(shí)候,說(shuō)明它已經(jīng)完全成功了,但是當(dāng)它回滾的時(shí)候,就說(shuō)明發(fā)生了一些錯(cuò)誤?;貪L會(huì)是下面2種情況:
    1        程序造成的回滾(程序回滾)
    2        非程序造成的回滾(非程序回滾)
    通常,程序回滾是因?yàn)樯虡I(yè)的規(guī)定。比如一個(gè)WEB程序做一個(gè)素描畫(huà)的價(jià)格的調(diào)查,程序可能讓用戶(hù)輸入年齡,并且商業(yè)規(guī)定18歲以上才可以進(jìn)入。如果一個(gè)16歲的提交了信息,那么程序就會(huì)拋出一個(gè)錯(cuò)誤,打開(kāi)一個(gè)網(wǎng)頁(yè)告訴他,他年齡還不能參與到這個(gè)信息的調(diào)查。因?yàn)槌绦驋伋隽水惓?,因此包含在程序中的事?wù)的就會(huì)發(fā)生回滾。這只是普通的程序回滾,只有當(dāng)發(fā)生大量的程序回滾才值得我們注意。
    另一方面,非程序回滾是非常糟糕的。有三種情形的非程序回滾:
    1        系統(tǒng)回滾
    2        超時(shí)回滾
    3        資源回滾
    系統(tǒng)回滾意味著程序服務(wù)器中的一些東西非常的糟糕,恢復(fù)的幾率很渺茫。超時(shí)回滾就是當(dāng)程序服務(wù)器中的程序處理請(qǐng)求時(shí)超時(shí);除非你把超時(shí)設(shè)置的很短才會(huì)出現(xiàn)這種錯(cuò)誤。資源回滾就是當(dāng)一個(gè)程序服務(wù)器管理內(nèi)部的資源的時(shí)候發(fā)生錯(cuò)誤。例如,如果你設(shè)置你的程序服務(wù)器通過(guò)一個(gè)簡(jiǎn)單的SQL語(yǔ)句去測(cè)試數(shù)據(jù)庫(kù)的連接,但數(shù)據(jù)庫(kù)對(duì)于程序服務(wù)器來(lái)說(shuō)是無(wú)法連接的,這個(gè)時(shí)候任何和這個(gè)資源相關(guān)的事情都會(huì)發(fā)生資源回滾。
    如果發(fā)生非程序回滾,我們應(yīng)該立刻注意,這個(gè)是不小的問(wèn)題,但是你也需要留意程序回滾發(fā)生的頻率。很多時(shí)候人們對(duì)發(fā)生的異常很敏感,因此你需要哪些異常對(duì)你程序來(lái)說(shuō)才是重要的。

    總結(jié)
    盡管各個(gè)程序和他們的環(huán)境都各不相同,但是有一些共同的問(wèn)題困擾著他們。這篇文章的注意力并不是放在程序代碼的問(wèn)題上,因?yàn)榘炎⒁饬Ψ旁谝驗(yàn)榄h(huán)境的問(wèn)題而導(dǎo)致的低性能的問(wèn)題上:
    1.內(nèi)存溢出
    2.線(xiàn)程池大小
    3JDBC連接池大小
    4JDBC預(yù)先聲明語(yǔ)句緩存大小
    5.緩存大小
    6.池大小
    7.執(zhí)行事務(wù)時(shí)候的回滾
    為了有效的診斷性能的問(wèn)題,你應(yīng)該了解什么問(wèn)題會(huì)導(dǎo)致什么樣的癥狀。如果主要是程序的代碼導(dǎo)致的惡果那你應(yīng)該帶著問(wèn)題去尋求負(fù)責(zé)代碼的人尋求幫助,但是如果問(wèn)題是由環(huán)境引起的,那么就要依靠你的操作來(lái)解決了。
    問(wèn)題的根源依賴(lài)于很多要素,但是一些指示器可以增加一些你處理問(wèn)題時(shí)候的一些信心,依靠他們可以完全排除一些其他的原因。我希望這個(gè)文章能對(duì)你排解JAVAEE環(huán)境問(wèn)題起到幫助。

     轉(zhuǎn)自

    posted on 2014-10-22 13:55 wilsonyq 閱讀(181) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): Java 、javaweb
    主站蜘蛛池模板: 啦啦啦完整版免费视频在线观看| 91亚洲国产成人久久精品| 免费国产在线精品一区| 日韩毛片免费在线观看| 亚洲女人被黑人巨大进入| 亚洲综合在线一区二区三区| 99热在线精品免费全部my| 亚洲AV美女一区二区三区| 免费萌白酱国产一区二区三区| 亚洲宅男天堂在线观看无病毒| 久久水蜜桃亚洲AV无码精品| 日韩免费电影在线观看| 老子影院午夜伦不卡亚洲| 久久精品女人天堂AV免费观看| 亚洲国产精品午夜电影| 国产精品久久久久免费a∨| 亚洲日本人成中文字幕| 亚洲免费视频观看| 亚洲日本乱码卡2卡3卡新区| 久久久久久99av无码免费网站| 亚洲JIZZJIZZ妇女| 一本久到久久亚洲综合| jizzjizz亚洲日本少妇| 亚洲AV无码专区日韩| 国产精品亚洲综合| 免费无码成人AV片在线在线播放| 亚洲精品无AMM毛片| 免费在线观看污网站| 亚洲AV无码国产一区二区三区| 又大又黄又粗又爽的免费视频| 四虎国产精品成人免费久久| 亚洲人成网77777色在线播放 | 久久久久久久岛国免费播放| 久久久久久亚洲精品成人| 97免费人妻无码视频| 亚洲性色AV日韩在线观看| 免费看一级做a爰片久久| 亚洲AV无码一区二区三区电影| 免费a级毛片无码a∨性按摩| 久久久久女教师免费一区| 综合亚洲伊人午夜网|