国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

成為JavaGC專家(5)—Java性能調(diào)優(yōu)原則

2019-11-14 15:24:40
字體:
供稿:網(wǎng)友

并不是每個程序都需要調(diào)優(yōu)。如果一個程序性能表現(xiàn)和預(yù)期一樣,你不必付出額外的精力去提高它的性能。然而,在程序調(diào)試完成之后,很難馬上就滿足它的性能需求,于是就有了調(diào)優(yōu)這項工作。無論哪種編程語言,對應(yīng)用程序進行調(diào)優(yōu)都需要豐富的技術(shù)知識并且注意力高度集中。另外,你也不應(yīng)該用相同的方式對兩個程序調(diào)優(yōu),因為每個程序都有它自己獨特的運作方式和不同的資源使用方式。正因如此,調(diào)優(yōu)比寫程序需要更多基礎(chǔ)知識。例如,你需要熟悉虛擬機、操作系統(tǒng)和計算機架構(gòu)。而當(dāng)你面對在這些知識基礎(chǔ)上編寫的程序時,就能成功地對它進行調(diào)優(yōu)。

有時調(diào)優(yōu)java程序只需要修改JVM參數(shù),比如GC的參數(shù)。但也有些時候需要修改程序代碼。無論那種方法,你首先都需要監(jiān)控執(zhí)行Java程序的進程。因此本文會講解下面幾個問題:

  • 怎樣監(jiān)控Java程序?
  • 應(yīng)該給JVM設(shè)置怎樣的參數(shù)?
  • 如何確定是否需要修改代碼?

對Java程序進行調(diào)優(yōu)的必要知識

Java程序在Java虛擬機中運行。因此為了進行調(diào)優(yōu),你需要理解JVM的工作流程。我之前有一篇博文Understanding JVM Internals,將讓你對JVM有深入的了解。

本文中有關(guān)JVM運作過程的知識主要關(guān)于GC和Hotspot。盡管只有這兩方面的知識可能無法對所有的Java程序進行調(diào)優(yōu),但是這兩個因素在大多數(shù)情況下都影響著Java程序的性能。

值得注意的是,從操作系統(tǒng)的角度來看,JVM也是一個應(yīng)用程序進程。為了給JVM創(chuàng)造良好的運行環(huán)境,你還需要對操作系統(tǒng)分配資源的過程有所了解。這意味著,想要調(diào)優(yōu)Java程序,除了JVM你也應(yīng)該理解操作系統(tǒng)或者硬件的工作方式。

需要具有的知識還有Java這門語言本身。另外理解鎖和并發(fā)、類加載和對象創(chuàng)建都是非常重要的。

當(dāng)開始調(diào)優(yōu)Java程序時,你應(yīng)該整合以上各方面的知識來完成工作。

Java程序性能調(diào)優(yōu)的過程

圖1是一張Java程序性能調(diào)優(yōu)的流程圖,摘自由Charlie Hunt和Binu John所著的Java Performance

圖1:Java程序性能調(diào)優(yōu)的過程

JVM分布式模型

JVM分布式模型用于決定是在一個JVM還是多個JVM上執(zhí)行Java程序。你可以根據(jù)其有效性、響應(yīng)能力和可維護性來進行選擇。當(dāng)在多臺服務(wù)器上運行JVM時,你也可以選擇將多個JVM運行于一臺服務(wù)器或者每臺服務(wù)器運行一個JVM。例如,對于每臺服務(wù)器,你可以運行一個使用8GB堆內(nèi)存的JVM,也可以運行4個使用2GB的JVM。你理應(yīng)根據(jù)處理器內(nèi)核的個數(shù)還有程序的特性來決定這個數(shù)量。當(dāng)優(yōu)先考慮響應(yīng)能力時, 使用2GB的堆內(nèi)存會優(yōu)于8GB的,原因是這樣能在更短的時間內(nèi)完成Full GC。當(dāng)然,8GB的堆內(nèi)存可以降低Full GC的頻率。如果你的程序使用了內(nèi)部緩存,還可以通過增加緩存命中率來提高響應(yīng)能力。綜上所述,選擇合適的模型需要考慮應(yīng)用程序的特性,然后在各種模型中 選定一個能夠揚長避短的。

JVM架構(gòu)

選擇JVM其實就是決定使用32位還是64位的JVM。在相同的條件下,你最好用32位的。因為32位的JVM比64位性能更好。然而,32位 JVM最大支持的堆內(nèi)存是4GB(無論在32位操作系統(tǒng)還是64位的上,實際可分配的大小都只有2-3GB)。如果需要更大的堆內(nèi)存,還是用64位的 JVM比較合適。

表1:性能比較(數(shù)據(jù)來源)

測試基準時間(秒)系數(shù)
C++ Opt231.0x
C++ Dbg1978.6x
Java 64-bit1345.8x
Java 32-bit29012.6x
Java 32-bit GC*1064.6x
Java 32-bit SPEC GC*893.7x
Scala823.6x
Scala low-level*672.9x
Scala low-level GC*582.5x
Go 6g1617.0x
Go PRo*1265.5x

下一步就是運行程序來測試它的性能。這個過程包括GC調(diào)優(yōu)、改變操作系統(tǒng)設(shè)置和修改代碼。對于這些工作,你可以使用系統(tǒng)監(jiān)視工具或者性能分析工具。

注意:針對響應(yīng)能力的調(diào)優(yōu)和針對吞吐量的調(diào)優(yōu)可能使用不同的方法。如果經(jīng)常性地發(fā)生stop-the-Word(串行GC暫時中斷程序執(zhí)行),程序的響應(yīng)能力就會被降低。比如在高吞吐量時執(zhí)行Full GC。不要忘記,在調(diào)優(yōu)時往往有得有失。這樣需要折衷處理的事情不僅發(fā)生在響應(yīng)能力和吞吐量之間。例如使用更多的CPU資源來降低內(nèi)存的使用,或者不得不忍受響應(yīng)能力和吞吐量其中一個性能指標的下降。相反的情況同樣可能發(fā)生,實際的調(diào)優(yōu)應(yīng)該根據(jù)各指標的優(yōu)先級來執(zhí)行。

上面圖1中的流程展示了幾乎可用于所有Java程序的性能調(diào)優(yōu)過程,包括Swing應(yīng)用。然而,對于我們公司NHN用于提供網(wǎng)絡(luò)服務(wù)的服務(wù)器端程序來說,這個方法多少有些不合適。下面圖2中的流程是根據(jù)圖1修改而來,它更簡單,也更適合NHN。

圖2:對HNH的Java程序的調(diào)優(yōu)過程

其中,Select JVM表示盡可能使用32位的JVM,除非你需要用64位的JVM來維護一個數(shù)GB的緩存。

現(xiàn)在,跟隨圖2中的流程,你會了解到每一步具體的工作。

JVM參數(shù)

我會主要講解如何為Web服務(wù)端程序設(shè)置合適的JVM參數(shù)。盡管不一定適合所有的案例,但是最好的GC算法是Concurrent Mark Sweep(CMS垃圾回收),特別是對于Web服務(wù)端程序。因為低延遲是非常重要的。當(dāng)然,在使用CMS時,由于新生代空間(New Area)的分配,可能發(fā)生較長時間的stop-the-world現(xiàn)象,不過調(diào)整新生代空間的大小或者它和整個堆空間的比例可能解決這個問題。

指定新生代空間的大小和指定整個對堆內(nèi)存的大小同樣重要。你最好使用–XX:NewRatio來指定新生代和整個堆的大小比例,或者直接用–XX:NewSize來指定所需的新生代空間。這個配置是非常必要的,因為大部分對象都不會存活很久。在Web程序中,除了緩存數(shù)據(jù),其他多數(shù)對象都只在HttpRequestHttpResponse期間創(chuàng)建。這個時間幾乎不會超過1秒,表示這些對象的存活時間也不會超過1秒。如果新生代空間不夠大,對象會被轉(zhuǎn)移到老年代空間,以便騰出地方給新對象使用。老年代空間(Old Area)垃圾回收的代價是比新生代空間大的多的,因此很需要設(shè)置一個充足的新生代空間。

然而,當(dāng)新生代空間的大小超過一個特定的水平,程序的響應(yīng)能力會被降低。因為新生代空間的垃圾回收過程,基本上是將數(shù)據(jù)從一個Survivor Area復(fù)制到另外一個(From Space和To Space)。另外,stop-the-world的現(xiàn)象在新生代空間和老年代空間執(zhí)行垃圾回收時都會發(fā)生。如果新生代空間變大,那么Survivor Area的空間也會更大,于是每次復(fù)制的數(shù)據(jù)就更多。基于這樣一種特性,我們應(yīng)該通過指定不同操作系統(tǒng)中HotSpot JVM的NewRatio參數(shù)來分配合適大小的新生代空間。

表2:不同操作系統(tǒng)和配置下NewRatio的默認值

操作系統(tǒng)及參數(shù)默認-XX:NewRatio
Sparc -server2
Sparc -client8
x86 -server8
x86 -client12

如果設(shè)置了NewRatio,那么整個堆空間的1/(NewRatio +1)就是新生代空間的大小。上表可以看出Sparc -server的NewRatio默認值很小,因為相比x86的操作系統(tǒng),Sparc以前更多用于高端應(yīng)用,這個值就是為它們設(shè)置的。但現(xiàn)在x86操作系統(tǒng)的性能有很大提升,使用它們作為服務(wù)器已經(jīng)很普遍了。因此指定NewRatio為2或者3是更好的選擇,就和Sparc -server上的配置一樣。

另外,你還可以通過指定NewSizeMaxNewSize來代替NewRatio。那么新生代空間創(chuàng)建時的大小就是指定的NewSize,隨后可以一直增長到MaxNewSize的值。Eden(新創(chuàng)建對象存放的區(qū)域)和Survivor Area兩個區(qū)域會隨比例增加。就和你為-Xms(譯者注:原文是-Xs,應(yīng)該是筆誤)和-Xmx設(shè)置相同的值一樣,將MaxSize和 MaxNewSize設(shè)置為相同的也是一個好選擇。

如果同時指定了NewRatio和NewSize,你應(yīng)該使用更大的那個。于是,當(dāng)堆空間被創(chuàng)建時,你可以用過下面的表達式計算初始新生代空間的大小:

1
min(MaxNewSize, max(NewSize, heap/(NewRatio+1)))

無論如何,僅通過一次嘗試就找到合適的堆空間和新生代空間大小是不可能的。根據(jù)我在NHN運行Web服務(wù)器的經(jīng)驗,建議使用下面的JVM參數(shù)來運行Java程序。監(jiān)控在這些參數(shù)的條件下程序的性能表現(xiàn)之后,你就能夠選擇更合適的GC算法或者配置。

表3:推薦的JVM參數(shù)

類型參數(shù)
運行模式-sever
整個堆內(nèi)存大小為-Xms和-Xmx設(shè)置相同的值。
新生代空間大小-XX:NewRatio: 2到4. -XX:NewSize=? –XX:MaxNewSize=?. 使用NewSize代替NewRatio也是可以的。
持久代空間大小-XX:PermSize=256m -XX:MaxPermSize=256m. 設(shè)置一個在運行中不會出現(xiàn)問題的值即可,這個參數(shù)不影響性能。
GC日志-Xloggc:$CATALINA_BASE/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps. 記錄GC日志并不會特別地影響Java程序性能,推薦你盡可能記錄日志。
GC算法-XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75. 一般來說推薦使用這些配置,但是根據(jù)程序不同的特性,其他的也有可能更好。
發(fā)生OOM時創(chuàng)建堆內(nèi)存轉(zhuǎn)儲文件-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$CATALINA_BASE/logs
發(fā)生OOM后的操作-XX:OnOutOfMemoryError=$CATALINA_HOME/bin/stop.sh 或 -XX:OnOutOfMemoryError=$CATALINA_HOME/bin/restart.sh. 記錄內(nèi)存轉(zhuǎn)儲文件后,為了管理的需要執(zhí)行一個合適的操作。

測定程序的性能

為了得到程序的性能表現(xiàn),需要以下這些信息:

  • 系統(tǒng)吞吐量(TPS、OPS):從整體概念上理解程序的性能。
  • 每秒請求數(shù)(Request Per Second – RPS):嚴格來說,RPS和單純的響應(yīng)能力是不同的,但是你可以把它理解為響應(yīng)能力。通過這個指標,你能夠了解到用戶需要多長時間才能得到請求的結(jié)果。
  • RPS的標準差:如果可能的話,還有必要包括事件的RPS。一旦出現(xiàn)了偏差,你應(yīng)該檢查GC或者網(wǎng)絡(luò)系統(tǒng)。

為了得到更準確的性能表現(xiàn),你應(yīng)該等到程序徹底啟動完成后再進行測量,因為字節(jié)碼隨后會被HotSpot JIT編譯為本地機器碼。總體來說,需要在程序加載完指定功能后,用nGrinder等工具測試至少10分鐘。

切實地調(diào)優(yōu)

如果nGrinder測試的結(jié)果滿足了預(yù)期,那么你不需要對程序進行性能調(diào)優(yōu)。如果沒有達到預(yù)期結(jié)果,你就應(yīng)該執(zhí)行調(diào)優(yōu)來解決問題。接下來會通過實例講解方法。

stop-the-world耗時過長

stop-the-world耗時過長可能是由于GC參數(shù)不合理或者代碼實現(xiàn)不正確。你可以通過分析工具或堆內(nèi)存轉(zhuǎn)儲文件(Heap dump)來定位問題,比如檢查堆內(nèi)存中對象的類型和數(shù)量。如果在其中找到了很多不必要的對象,那么最好去改進代碼。如果沒有發(fā)現(xiàn)創(chuàng)建對象的過程中有特別的問題,那么最好單純地修改GC參數(shù)。

為了適當(dāng)?shù)卣{(diào)整GC參數(shù),你需要獲取一段足夠長時間的GC日志,還必須知道哪些情況會導(dǎo)致長時間的stop-the-world。想了解更多關(guān)于如何選擇合適的GC參數(shù),可以閱讀我同事的一篇博文:How to Monitor Java Garbage Collection。

CPU使用率過低

當(dāng)系統(tǒng)發(fā)生阻塞,吞吐量和CPU使用率都會降低。這可能是由于網(wǎng)絡(luò)系統(tǒng)或者并發(fā)的問題。為了解決這個問題,你可以分析線程轉(zhuǎn)儲信息(Thread dump)或者使用分析工具。閱讀這篇文章可以獲得更多關(guān)于線程轉(zhuǎn)儲分析的知識:How to Analyze Java Thread Dumps。

你可以使用商業(yè)的分析工具對線程鎖進行精確的分析,不過大部分時候,只需使用JVisualVM中的CPU分析器,就能獲得足夠的信息。

CPU使用率過高

如果吞吐量很低但是CPU使用率卻很高,很可能是低效率代碼導(dǎo)致的。這種情況下,你應(yīng)該使用分析工具定位代碼中性能的瓶頸。可使用的工具有:JVisualVMEclipse TPTP或者JProbe

調(diào)優(yōu)方法

建議你使用如下方法對程序進行調(diào)優(yōu)。

首先,檢查性能調(diào)優(yōu)是否必要。測量性能不是一件簡單的工作,你也不能保證每次都獲得滿意的結(jié)果。因此如果程序已經(jīng)滿足預(yù)期性能需求,不必在調(diào)優(yōu)上增加額外的投入了。

問題只出在一個地方,你要做的就是去解決掉它。二八定律(Pareto principle)對性能調(diào)優(yōu)同樣適用。這不是說某個模塊的低性能一定只源于一個問題,而是強調(diào)我們應(yīng)該在調(diào)優(yōu)時把注意力放在影響最大的那個問題上。在處理好了最重要的之后,你才應(yīng)該去解決剩下其他的。也就是建議一次只對一個問題進行修復(fù)。

另外需要考慮到氣球效應(yīng)(Balloon effect),有得必有失。你可以通過使用緩存來提高響應(yīng)能力,但是當(dāng)緩存逐漸增大,執(zhí)行一次Full GC的時間也會更長。一般而言,如果你希望內(nèi)存使用率比較低,那么吞吐量和響應(yīng)能力可能都會惡化。因此,要知道什么對自己程序來說最重要的,而哪些又是次要的。

到此為止,你應(yīng)該已經(jīng)了解了如何對Java程序進行性能調(diào)優(yōu)。為了介紹性能測定的具體過程,我不得不省略其中一些細節(jié),不過我認為這些也足夠應(yīng)對大多數(shù)Java Web服務(wù)端程序了。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 浙江省| 黎川县| 怀集县| 启东市| 大石桥市| 牙克石市| 肇源县| 黔西| 富源县| 赤壁市| 陇川县| 县级市| 宜春市| 凤阳县| 平遥县| 莆田市| 康保县| 定州市| 临澧县| 乐陵市| 灵丘县| 措勤县| 濮阳县| 玛多县| 朝阳县| 任丘市| 大英县| 疏附县| 公主岭市| 桐城市| 徐汇区| 阿鲁科尔沁旗| 孟津县| 松溪县| 手游| 长垣县| 宁强县| 鄱阳县| 梁山县| 天全县| 彭州市|