摘要:在本文中我將會介紹應用性能優化的一般原則。性能優化的流程圖摘取自和合著的性能,描述了應用性能優化的處理流程。例如,對每臺服務器,你面臨著為單個分配堆內存和運行個并為每個分配堆內存的選擇。不過位能使用堆內存最大理論值只有。
原文鏈接:http://www.cubrid.org/blog/dev-platform/the-principles-of-java-application-performance-tuning/
本文是GC專家系列中的第五篇。在第一篇理解Java垃圾回收中我們學習了幾種不同的GC算法的處理過程,GC的工作方式,新生代與老年代的區別。所以,你應該已經了解了JDK 7中的5種GC類型,以及每種GC對性能的影響。
在第二篇Java垃圾回收的監控中介紹了在真實場景中JVM是如何運行GC,如何監控GC數據以及有哪些工具可用來方便進行GC監控。
在第三篇GC 調優中基于真實案例介紹了可用于GC調優的最佳選項。同時也描述了如何通過降低移動到老年代中對象的數量來縮短Full GC耗時,以及如何設置GC類型及內存大小。
在第四篇Apache的MaxClients設置及其對Tomcat Full GC的影響中介紹了Apache對MaxClients選項在系統發生GC時對整體性能的影響。
在本文中我將會介紹Java應用性能優化的一般原則。具體來說,我會介紹性能優化的必要條件、判斷是否需要優化的步驟,同時也會列出在性能優化過程中經遇到的一些問題。在文章結尾,我會給你一些在性能優化過程中如何做出最優決定的建議。
概述不是每個應用都需要優化。如果系統的運行狀況正如你的期望,你就沒必要花費更多精力在額外的性能提升上。然而,在調試過程中就期望系統能達到它的目標性能往往會比較困難。這時就需要做系統優化的工作了。不管使用哪種語言,性能優化都要有較高的專業技能和高度專注。另外,因為每個應用都有自己獨特的操作和不同的資源使用情況,在優化兩個不同系統中可能需要使用不同的具體方法。所以與開發應用相比,性能優化更需要有扎實的基礎知識,例如需要具有虛擬機、操作系統甚至計算機體系結構的相關知識。基于這些基礎,再面對系統進行優化時,成功的機率就會更高。
一些Java應用的優化只需要調整JVM的選項,例如改變垃圾回收類型,不過有時也是需要去調整源碼。不管使用哪種方式,你首先都需要去監控Java應用的執行處理過程。基于此,本文主要涵蓋的內容如下:
如何監控Java應用
如何設置JVM選項
如何判斷是否有必要修改應用代碼
Java性能優化必備的基礎知識Java應用在JVM中運行,因此優化Java應用,你需要理解JVM的運行過程。在前面的文章深入理解JVM你可以找到一些關于JVM重要概念的介紹。
在本文中關于JVM運行過程的講解著重于垃圾收集(GC)和 Hotspot相關知識。為了構造一個使JVM 運行良好的環境,你需要理解操作如何為進程分配資源。所以即便是優化Java應用,你也需要像熟悉JVM一樣去熟悉操作系統甚至硬件知識。
與Java語言相關的知識也十分重要。同樣理解鎖和并發、熟悉類的加載與對象創建都是應該具備的技能。
一旦將Java應用優化付諸行動,你就需要綜合利用上面提到的相關知識進行全面分析。
Java性能優化的流程圖1摘取自Charlie Hunt和Binu John合著的《Java性能》,描述了Java應用性能優化的處理流程。
圖1: Java應用性能優化流程
上圖并不是一個一次性流程,在性能優化完成之前你可能需要重復其中的過程。此過程同樣適用于如何選取一個期望的性能指標。在優化過程中,有時需要降低性能指標的預期值,有時則需要提高性能指標的預期值。
JVM部署模型JVM部署模型關系到如何決定是否把應用部署到單個或多個JVM上運行。這可以從系統的可用性、響應速度和可維護性上來做取舍。即便是決定了使用多個JVM,你也還需要確定在單臺服務器上運行多個JVM或者是每臺服務器上運行一個JVM。例如,對每臺服務器,你面臨著為單個JVM分配8GB堆內存和運行4個JVM并為每個JVM分配2GB堆內存的選擇。當然單臺服務器運行的JVM的數量也取決于CPU的核數以及應用本身的特點。在對比以上兩個配置的響應速度時,具有2GB堆空間的方案可能更有優勢,因為使用2GB的堆空間比使用8GB堆空間在Full GC時耗時更短。不過話說回來,使用8GB堆空間卻可以減少Full GC的頻率。另外也可以通過提高應用內部緩存命中率的方式來提高系統響應速度。所以,最終選擇部署模型需要綜合考慮應用的特點和所選方案對應用帶來的優劣對比。
JVM體系結構選擇JVM時還需要面臨32位JVM和64位JVM。同樣條件下,應該優化選擇32位JVM,因為32位JVM比64位的表現更優。不過32位JVM能使用堆內存最大理論值只有4GB。(事實上,32位操作系統和64位操作系統能分配的空間大小都只有2-3GB)。當堆空間需求更大時,使用64位JVM會是更好的選擇。
表 1:性能對比
Benchmark | Time (sec) | Factor |
---|---|---|
C++ Opt | 23 | 1.0x |
C++ Dbg | 197 | 8.6x |
Java 64-bit | 134 | 5.8x |
Java 32-bit | 290 | 12.6x |
Java 32-bit GC* | 106 | 4.6x |
Java 32-bit SPEC GC* | 89 | 3.7x |
Scala | 82 | 3.6x |
Scala low-level* | 67 | 2.9x |
Scala low-level GC* | 58 | 2.5x |
Go 6g | 161 | 7.0x |
Go Pro* | 126 | 5.5x |
接下來要做的就是運行應用并衡量其性能。這些過程包括GC調優、調整操作系統設置以及修改應用代碼。在這些過程中,你需要使用一些系統監控工具或者程序分析工具來幫你完成任務。
值得注意的是為響應速度的優化和為吞吐量的優化途徑可能會截然不同。例如,不時發生的stop-the-world會降低響應速度,而Full GC則會導致單位時間內的吞吐量量大幅減少。所以其中必定會有所權衡。當然這些權衡不只發生于響應速度和呑吐量之間,你可能需要使用更多的CPU資源來減少內存使用來以避免響應速度或吞吐量的降低。與此相反的場景也同樣會發生,你需要按一定的優先順序來解決。
圖1中的性能優化流程圖適用于包括Swing應用在內的幾乎所有Java應用。盡管如此,這個流程并不太適用于我們NHN公司為網絡服務編寫服務器應用的場景。下圖2是針對NHN公司并基于圖1制定的一個簡化的處理流程。
圖2:NHN公司的推薦的Java應用優化過程
上圖中的選擇JVM(Select JVM)是說通常32位JVM就足夠了,除非你需要使用JVM維護幾個GB的緩存數據。
好了,基于圖 2中的流程,你將開始學到處理每一步中所需應對的事情。
JVM選項我將主要介紹如何為Web應用服務器設置合適的JVM參數。盡管不能窮盡所有案例,但最優的GC算法,尤其針對Web應用,通常是CMS GC,這主要是因為Web應用的低延遲要求決定的。當然在使用CMS過程中,有時會遇到因為過多的內存碎片導致的較長時間的stop-the-world現象發生。不過這個問題可以通過調整新生代大小或者碎片比例進行優化。
設置新生代大小和設置整個堆大小一樣重要。最好通過-XX:NewRatio參數設置新生代空間與整個堆空間的大小比例,或者通過-XX:NewSize來多帶帶設置期望的新生代空間。設置新生代空間的重要性是因為大多數對象的存活時間很短。在Web應用中,除了緩存之外的大多數對象,是在與HttpRequest相應的HttpResponse創建的時候產生的,而這個過程很少會超過1秒,也就是說其中的對象的生命周期也不會超過1秒。如果新生代空間設置不夠大,當需要創建新對象時,舊的對象就需要移到老年代。老年代的GC開銷卻比新生代GC開銷大得多,因此設置恰當的新生代空間是十分重要的。
盡管如此,如果新生代空間超過一定比例,系統的影響速度將會降低。因為新生代垃圾回收的基本過程就把對象從一個存活區(Survivor area)復制到另外一個存活區。所以像老年代一樣,在新生代執行GC過程中也同樣會發生stop-the-world現象。如果新生代設置變大,存活區的空間相應也會增加,結果就是需要復制的數據空間將增加。基于這些特點,根據操作系統不同,通過NewRatio選項為HotSpot JVM設置合適的新生代空間是很有必要的。
表2: 不同操作系統與JVM選項的NewRatio默認值
OS and option | Default -XX:NewRatio |
---|---|
Sparc -server | 2 |
Sparc -client | 8 |
x86 -server | 8 |
x86 -client | 12 |
如果設置了NewRatio,則將有1/(NewRatio + 1)的堆空間屬于新生代。你會發現上表中Sparc -server的NewRatio的值非常小,因為當使用上面的默認值時,Sparc系統是用在比 x86更高端的場景中。因為x86性能的提升,目前使用x86 server也變得更為常見,像Sparc -server一樣設置NewRatio的值為2或3也更為合理。
除此之外,你也可以使用NewSize和MaxNewSize作為NewRatio的替代使用。新生代空間初始大小由NewSize設定,并且隨著內存消耗,新生代空間最大可擴展到MaxNewSize的大小。隨著NewRatio的變化,Eden和Survivor區域的大小也在發生變化。正如通過相同-Xms和-Xmx為堆空間設置固定值,為新生代設置相同的MaxSize和MaxNewSize也是一個不錯的選擇。
如果同時設置了NewRatio和NewSize,其中較大的值會起作用。所以當一個堆空間創建之后,就可以通過如下公式計算初始新生代空間的大小:
min(MaxNewSize, max(NewSize, heap/(NewRatio + 1)))
不過在優化過程中,無乎不可能一下子就為堆大小和新生代大小找到了恰當的值。基于我在NHN運行Web應用程序的經驗,我推薦在啟動Java應用時使用如下JVM選項。在經過對這些選項的性能監控結果分析之后,你會找到更合適的GC算法或選項。
表3:推薦的JVM選項
選項類型 | 選項 |
---|---|
運行模式 | -server |
堆大小 | 指定相同的-Xms和-Xmx |
新生代大小 | -XX:NewRatio: 取值在2-4之間 |
-XX:NewSize=?, -XX:MaxNewSize=?。使用NewSize替代NewRatio也是不錯的選擇 | |
永久代大小 | -XX:PermSize = 256m -XX:MaxPermSize=256m 把永久代大小設置為一個運行時不會出錯的大小,因為它并不影響系統的性能 |
GC 日志 | -Xloggc:$CATALANA_HOME/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps。輸出GC日志并不明顯影響應用性能,因此推薦保留詳細的GC日志信息。 |
GC 算法 | -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75。這只是一個推薦的通用配置。根據應用特點不同,其他配置也許更優。 |
OOM發生時輸出堆dump | -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$CATALINA_HOME/logs |
OOM發生后的執行動作 | -XX:OnOutOfMemoryError=$CATALINA_HOME/bin/stop.sh 或者 -XX:OnOutOfMemoryError=$CATALINA_HOME/bin/restart.sh。OOM之后除了保留堆dump外,根據管理策略選擇合適的運行腳本。 |
需要獲取能反映應用性能的幾個關鍵信息如下:
TPS(OPS):這個信息用于從概念上理解應用的性能。
Request Per Second(RPS):嚴格來說,RPS并不同于響應速度,但你可以把它理解為響應速度。通過RPS,你可以檢查用戶獲取請求結果所耗費的時間。
RPS 標準偏差(RPS Standard Deviation):如果有可以,盡量保持RPS的穩定。如果出現偏差,則需要檢查是否需要做GC優化或者是否有內部系統問題。
為了獲取盡可能精確的性能結果,首先要對應用進行充分的預熱,待穩定之后再開始性能測量,因為這時字節碼已被HotSpot JIT進行了編譯。通常,在使用nGrinder工具做負載測試時,至少要等系統達到某個負載水平10分鐘后再測量系統的實際性能。
在關鍵點上做優化如果nGrinder的測試結果滿足預期,那就不需要對應用進行優化。如果性能遜于預期,則需要開始優化以解決問題。下面通過具體案例來看性能優化的方法。
Stop-the-World耗時過長長時間的stop-the-world通常是由于使用了不恰當的GC選項或者不正確的應用實現所致。通常可以通過分析工具(profiler)或者堆dump的結果判斷導致stop-the-world的原因。也就是說可以通過檢查堆中對象的類型和數量判斷問題原因。如果有過多非必須對象存在,則需要修改應用代碼優化實現。如果在創建對象過程中沒有明顯的問題,則需要調整GC選項。
為了把GC選項調整到恰當的設置,你需要有足夠長時間的GC日志,并從中找出在哪種狀況下出現了stop-the-world。關于選擇合適GC選項的具體細節,可參考Java 垃圾回收的監控。
CPU使用率過低當系統發生阻塞時,TPS和CPU使用率都會降低。問題可能來自于內部交互系統或者高并發。分析這種場景,可以對線程dump的結果進行分析或者使用分析工具(profiler)。線程dump的分析方法可以參考如何分析Java線程Dumps
使用一些商業分析工具(profiler),你可以得到非常具體的鎖相關的分析報告。不過,大多數場景只需要使用jvisualvm中的CPU分析器就可以獲得滿意的結果。
CPU使用率過高如果TPS很低,但CPU使用率卻非常高,就通常由于低效率的代碼實現所致。這種場景,也需要通過使用分析器找到瓶頸的位置。可用的分析工具有jvisuavm,Eclipse的TPTP或者使用JProbe。
優化的途徑關于應用優化的一些建議途徑如下:
首先,判斷是否有必要做性能優化。衡量系統的性能并非易事,任何時候都不能保證你能得到滿意的結果。所以如果應用已經達到了期望的目標性能,就沒必要投入精力做額外的優化。
問題就在那里,你需要做的是解決它。Pareto 法則同樣適用于性能優化。這并不是說一個特定的低性能表現只來源于一個問題,相反,在性能優化過程中,更應該把精力投入到對性能影響最大的那一點上。所以,當解決了最嚴重的問題后,就可以接著處理其他問題。不過建議是每次只著重解決一個問題。
你可能想到了氣球效應。為了實現一個目標,你需要決定放棄哪些。你可以通過使用緩存來提高響應速度,然而隨著緩存的增加,其Full GC所需耗時也將增加。一般來說,如果你想維持少量的內存使用,系統的呑吐量和響應時間將會受到影響。所以,你要清楚哪些是最重要的,哪些微不足道的。
到目前為止,你已經了解了Java應用性能優化的方法。為了介紹衡量性能的具體過程,我忽略了一些細節。盡管如此,我想本文已足夠應對Java Web應用的大多數優化場景。
作者:Se Hoon Park,網絡平臺開發實驗室高級軟件工程師,NHN公司
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/65403.html
摘要:調優調優中基于真實案例介紹了可用于調優的最佳選項。的設置及其對的影響的設置及其對的影響中介紹了對選項在系統發生時對整體性能的影響。具體來說,我會介紹性能優化的必要條件判斷是否需要優化的步驟,同時也會列出在性能優化過程中經遇到的一些問題。 1. 理解Java垃圾回收 理解Java垃圾回收中我們學習了幾種不同的GC算法的處理過程,GC的工作方式,新生代與老年代的區別。所以,你應該已經了解...
摘要:原文鏈接這是專家系列文章的第二篇。運行在本地虛擬機上的應用的又稱為,通常與相同。性能數據需要持續觀察,因此在運行時需要定時輸出的監控信息。新生代容量的統計信息。是提供的一個式的圖表監控工具。 原文鏈接:http://www.cubrid.org/blog/dev-platform/how-to-monitor-java-garbage-collection/ 這是GC專家系列文章的第二...
摘要:原文鏈接本篇是專家系列的第三篇。但是,請記住調優是不得已時的選擇。縮短耗時的單次執行與相比,耗時有較明顯的增加。創建文件過程中,進程會中斷,因此不要在正常運行時系統上做此操作。因此校驗結果并根據具體的服務需要,決定是否要進行調優。 原文鏈接:http://www.cubrid.org/blog/dev-platform/how-to-tune-java-garbage-collecti...
摘要:本文是成為專家系列的第一篇。然而,在多線程環境下,將會有別樣的狀況。在中正是通過解決了多線程問題。在最后的并發清理階段,垃圾回收過程被真正執行。在垃圾回收執行過程中,其他線程依然在執行。 原文鏈接:http://www.cubrid.org/blog/de... 了解Java的垃圾回收(GC)原理能給我們帶來什么好處?對于軟件工程師來說,滿足技術好奇心可算是一個,但重要的是理解GC能幫...
摘要:本文將介紹的參數的重要性以及在發生時對系統整體性能的顯著影響。我們來看下的選項在發生時會對系統帶來哪些影響。所以這些請求將會放到堆積隊列,隊列的長度是的中設置的。從而導致進程的數量超過,并觸發了操作系統進行內存交換的閥值。 原文鏈接:http://www.cubrid.org/blog/dev-platform/maxclients-in-apache-and-its-effect-o...
閱讀 3083·2021-11-24 11:14
閱讀 3530·2021-11-22 15:22
閱讀 3216·2021-09-27 13:36
閱讀 727·2021-08-31 14:29
閱讀 1336·2019-08-30 15:55
閱讀 1769·2019-08-29 17:29
閱讀 1154·2019-08-29 16:24
閱讀 2420·2019-08-26 13:48