1. 程式人生 > >JAVA虛擬機器(六)調優案例分析與實戰

JAVA虛擬機器(六)調優案例分析與實戰

一個線上文件網站採用了新的硬體,4個CPU,16GB實體記憶體。管理員為了儘量利用硬體資源選用了64位的JDK1.5,並且將堆的大小固定位12GB。但是網站不定期出現失去響應的情況。 監控伺服器發現是由於GC停頓導致的,回收12GB的堆,一次Full GC停頓高達14秒。而且因為記憶體中有很多文件序列化產生的大物件,他們直接進入到了老年代,沒有在Minor GC中清理掉。 問題點:過大的堆導致記憶體回收時帶來了長時間的停頓。升級前使用32位系統1.5GB的堆,使用者只感覺使用網站比較緩慢。 在高效能硬體上部署程式,目前主要有兩種方式: 1.使用64為JDK來使用大記憶體。 2.使用若干個32位虛擬機器建立邏輯叢集來利用硬體資源。 如果使用第一種,對於互動性強的應用,給Java虛擬機器分配超大堆的前提是有把握把程式Full GC的頻率控制的足夠低。並且需要面對幾個問題:記憶體回收導致長時間停頓,目前64位JDK的測試效能普遍低於32位的JDK。 現階段很多人還是選擇第二種方法,同一個物理機器上啟動多個應用伺服器程序,然後在前端搭建一個負載均衡器,以反向代理的方式分配訪問請求。可能會遇到的問題,儘量避免節點競爭全域性的資源,典型的是磁碟競爭。很難高效率的利用某些連線池。各個節點仍然受到32位的記憶體限制,32位windows平臺中每個程序只能使用2GB的記憶體。出去堆以外的開銷,最多隻能開到1.5GB。 最後的解決方案: 建立5個32為JDK邏輯叢集,每個程序按2GB記憶體計算,總共佔用10GB,並且文件服務的主要壓力集中在磁碟和記憶體訪問,CPU資源敏感度低,改用CMS收集器。

一個學校的小型專案,基於B/S的電子考試系統,客戶端實時的從服務端接收考試資料。堆記憶體給到了1.6GB(基本已經是windows系統最大的記憶體了)。最後仍然報出 java.lang.OutOfMemoryError at org.eclipse.jetty.io.nio.DirectNIOBuffer 許多人已經看出了問題所在,直接記憶體溢位,這塊直接記憶體並不算在1.6GB之內,因此它的最大也只在0.4GB的一部分。

在進行垃圾回收時,虛擬機器雖然會對Direct Memory進行回收,但是Direct Memory卻不能像新生代,老年代那樣,發現不足了就進行垃圾回收,他只能等待老年代滿了之後Full GC,然後順便幫他清理掉記憶體的廢棄物件。NIO操作就需要使用Direct Memory記憶體。

一個數字校園應用系統,執行在一個4個CPU的Solaris 10作業系統上。系統在做大併發壓力測試的時候,發現請求響應時間比較慢。CPU佔用率較高,但不是應用佔用。

最後檢視佔用CPU資源最多的是fork系統呼叫,“fork”系統呼叫時Linux用來產生新程序的,在Java虛擬機器中,使用者編寫的Java程式碼最多隻有執行緒概念,不應當有程序的產生。

原因是他們使用Java的 Runtime.getRuntime().exec()方法來呼叫外部shell指令碼,這個過程會頻繁的建立進行,資源消耗很大。 解決方案,去掉這個shell指令碼,使用Java API來獲取資訊。

一個基於B/S的MIS系統,硬體為2個CPU,8GB記憶體的系統,執行一段時間後,頻繁出現叢集節點虛擬機器自動關閉現象,留下一個hs_err_pid###.log後,程序就消失了。

從虛擬機器程序崩潰前不久,都發生過大量相同的異常。 java.net.SocketException:Connection reset 這時一個遠端連線異常,使用了非同步的方式呼叫Web服務,但是兩邊的速度不對等,時間越長就積累了越多的Web服務沒有呼叫完成,導致等待的執行緒和Socket連線越來越多,最終超過虛擬機器的承受能力使得虛擬機器程序崩潰。 解決思路,將非同步呼叫改為生產者/消費者模式的訊息佇列實現。

一個後臺RPC伺服器,採用64位虛擬機器,使用ParNew + CMS收集器組合,平時對外服務Minor GC的時間在30毫秒以內,完全可以接收,但在業務上,每10分鐘載入一個80MB的資料檔案到記憶體中,會形成超過100萬個HashMap,這時Minor GC就會造成500毫秒的停頓。

此處建立了太多HashMap物件,儲存效率也比較低。