垂直打擊之JVM剖析

讓Java應用程序運行是一回事,但讓他們跑得快就是另一回事了。在面對對象的環境中,性能問題就像來勢兇猛的野獸。但JVM的複雜性將性能調整的複雜程度增長了一個級別。這裏Refcard涵蓋了JVM internals、class loading(Java8中更新以映射最新的元空間)、垃圾回收、故障診斷、檢測、併發性,等等。html

介紹

Java是目前軟件開發領域中使用最普遍的編程語言之一。Java應用程序在許多垂直領域(銀行、電信、醫療保健等)中都有普遍使用。Refcard的目的是,幫助開發者經過專一於JVM內部,性能調整原則和最佳實踐,以及利用現有監測和故障診斷工具,來提高應用程序在商業環境中的性能。java

它能以不一樣的方式定義「optimal performance(最佳性能)」,但基本要素是:Java程序在業務響應時間要求內執行計算任務的能力,程序在高容量下執行業務功能的能力,並具備可靠性高和延遲低的特色。有時,數字自己變得模式化:對於一些大型網站,優秀的頁面響應時間應該在500ms如下。在適當的時候,Refcard包括目標數字。但在大多數狀況下,您須要根據業務需求和現有的性能基準本身決定這些。git

JVM Internals

基礎知識

代碼編譯和JIT

編譯Java字節碼顯然沒有直接從主機執行本機代碼那麼快。爲了提升性能,Hotspot JVM找出最繁忙的字節碼區域,而後將其編譯成更高效地原生、機器代碼(自適應優化)。而後這種本地代碼就會存儲在非堆內存中的代碼緩存中。apache

注意:多數的JVM是經過禁用JIT編譯器實現的(Djava.compiler=NONE)。您只須要考慮禁用的關鍵性優化,好比JVM崩潰。編程

下圖說明了Java源代碼,即時編譯流程和生命週期。windows

 

內存空間

HotSpot Java Virtual Machine是由如下的存儲空間組成。數組

存儲空間 描述
Java Heap Java程序類實例和數組的主存儲器。
Permanent Generation(JDK 1.7及如下版本)Metaspace (JDK 1.8及以上版本) Java類元數據的主存儲器。注意:從Java 8開始,PermGen空間就由元空間和使用本地存儲器替換了,相似於IBM J9 JVM。
Native Heap(C-Heap) 本地內存存儲線程、棧、包括對象的代碼緩存,如MMAP文件和第三方本機庫。

類加載

Java的另外一個重要特色是,在JVM啓動以後,它可以加載編譯的Java類(字節碼)。根據程序的大小,在剛剛重啓以後,程序在類加載過程當中性能會顯著下降。這種現象是由於內部JIT編譯器在重啓以後須要從新開始優化。緩存

自JDK 1.7版本以後,有一些改進值得你們重視。例如默認的JDK class loader具備更好的裝在類併發能力。安全

熱點

關注的區域 建議
JVM重啓後的性能降低 避免部署過量的Java類到一個單一的應用程序類加載器(例如:很是大的WAR文件)
運行時發現過多的類加載爭奪(thread lock, JAR file searches…) ,下降了總體性能。 分析您的應用程序並識別代碼模塊進行動態類加載操做過於頻繁。積極尋找非一站式類加載錯誤,如ClassNotFoundException和NoClassDefFoundError。再訪Java映射API和適用狀況下優化的過分使用。
java.lang.OutOfMemoryError: PermGen space (JDK 1.7及如下版本)java.lang.OutOfMemoryError:元空間(JDK 1.8及以上版本) 再訪JVM Permanent Generation、Metaspace (MaxMetaSpaceSize)和本地內存容量在適用狀況下的尺寸。分析應用程序類加載器和識別元數據的內存泄漏的源頭。

 

故障診斷和監視

目標 建議
跟蹤那些加載到不一樣的類加載器的Java類。 配置程序選擇使用的Java profiler,例如JProfiler或Java VisualVM。將重點放在類加載器的操做和內存佔用上。能夠經過–verbose:class. for the IBM JVM,生成多個Java核心快照跟蹤活動的類加載器和加載類。
調查類元數據的內存泄露的能夠來源。 配置程序和定義可能的culprit(s)。生成並分析JVmheap dump快照,專一於類加載器和java.lang.Class中的實例。

 

確保適當的Permanent Generation / Metaspace和本地內存大小。 密切監視你的PermGen、元空間和本機內存利用率,並調整到適合的最大容量。分析程序類加載器的大小,並尋找機會適當地減小元數據足跡。

垃圾回收

Java垃圾回收流程對於程序性能是相當重要的。爲了提供有效的垃圾回收,Heap(堆)本質上是劃分在子區域中。併發

堆區域

區域 描述
最新一代-Young Generation (nursery space) 新的或短暫的對象分配保留堆的一部分。垃圾被一個fast but stop-the-world YG的收集器進行回收。

 

在young space中呆了足夠久的對象就會提高到old space。

注意:YG space的尺寸和GC頻率太高將會顯著影響程序的響應時間,從而致使JVM的暫停時間增長。

老一代-Old Generation  (tenured space) heap的一部分留給了long-lived對象。垃圾一般經過平行或併發(多數時候)進行收集,諸如CMS或gencon (IBM JVM)。

 

性能提示:根據應用程序的需求選擇並測試最佳的GC策略是很是重要的。例如,當切換到併發GC收集(如CMS或G1)能夠顯著提升應用程序的平均響應時間(減小延遲)。

GC Collectors

選擇正確的collector或GC policy能夠將程序的性能、可擴展性和可靠性優化到最佳狀態。許多應用程序對於響應時間延遲都很敏感,所以大多須要使用併發的回收器,例如HotSpot CMS或IBM GC policy balanced。

咱們強烈建議您經過適當的性能和負載測試肯定最合適的GC策略。應該在生產環境中執行全面監控策略,以跟蹤總體的JVM性能,並肯定在以後須要改進的領域。

GC 論據 描述
串行回收器 -XX:+UseSerialGC (Oracle HotSpot) 不管新舊回收器都使用單獨CPU,像是一種stop the world的時尚。


並行回收器(吞吐量回收器) -XX:+UseParallelGC-XX:+UseParallelOldGC
(Oracle Hotspot)

 

-Xgcpolicy:optthruput
(IBmJ9, single space, stop-the-world)

旨在利用CPU的內核優點。不管新舊回收器都使用多個Gcthreads(via –XX:ParallelGCThreads=n),從而更好地利用來自主機的可用的CPU內核來完成。注意:雖然回收時間能夠顯著減小,可是有着大尺寸堆的程序面臨着large、stop-the-world、old回收,而且響應時間也受到影響。


確保適當的Permanent Generation / Metaspace和本地內存大小。 密切監視你的PermGen、元空間和本機內存利用率,並調整到適合的最大容量。分析程序類加載器的大小,並尋找機會適當地減小元數據足跡。 旨在最大限度地減小舊一代stop-the-world回收器對程序響應時間的影響。大多數使用CMS collector的老一代回收器與所述應用程序的執行同時進行。

 

注意:YoungGen collections仍然有stop-the-world事件,所以須要適當的微調,以減小總JVM暫停時間。

Garbage First (G1) Collector

HotSpot G1 collector是專爲是專爲知足用戶定義的垃圾回收(GC)高几率暫停時間設計的,同時實現高吞吐量。

最新的HotSpot collector將heap基本劃分到一組大小相等的堆區域,虛擬內存的每一個區域連續範圍。它將回收壓縮的活動集中在heap區域,那裏充滿了可回收的對象(garbage first)。換句話說就是,這個區域有最低限度的「live」對象。

Oracle建議在如下例子和狀況下使用G1 collector,尤爲是對於目前正在使用CMS或parallel collectors的:

  • 專爲large heaps(>= 6 GB),並限制GC延遲(暫停時間<= 0.5秒)的應用程序設計。
  • 超過50%的Java heap被實時數據佔用(對象不能被GC回收)。
  • 對象分析率和促進做用顯著變化。
  • 不指望過長的垃圾回收或壓縮停頓(超過0.5至1秒)。

Java Heap尺寸

你必定要知道沒有GC策略能夠挽救Java Heap尺寸不足的現象。這些演習涉及到爲不一樣的存儲空間(包括新舊不一樣的版本)配置最大和最小的容量,包括元數據和本地內存容量。這裏有一些建議準則:

  • 在32-bit或64-bit JVM之間進行明智的選擇。若是程序運行須要超過2GB內存,而且JVM暫停時間在可接受範圍內,能夠考慮使用64-bit JVM。
  • 永遠將應用程序放在第一考慮。確保將其配置好,並根據程序的內存佔用量調整heap尺寸。建議經過性能和負載測試來衡量實時數據佔有量。
  • larger heap並不老是表現得更好、更快,所以不須要過分調整Java heap。並行中的JVM性能調優,找準機會減小或「spread」程序的內存佔有量,以保證JVM的平均響應時間<1%。
  • 對於32-bit JVM,爲了從元數據和本地heap中留出一些內存,考慮2GB的最大heap尺寸。
  • 對於64-bit JVM,咱們要想辦法在垂直和水平層面進行擴展,而不是試圖將Java heap尺寸增長到15GB以上。這種作法每每提供更好的吞吐量,更好地利用硬件,提升應用程序的故障切換功能。
  • 不準重複開發:充分利用開源以及商業故障排除的優點和監控工具,使這些變成可能。APM(應用性能管理)產品在過去十年裏發展迅猛。

JDK 1.8 Metaspace指南

目標 建議
內存大小GC調整

 

監控和故障排除

默認狀況下,元空間內存空間是無界的,並使用可用於動態擴展的process或OS native memory。內存空間分紅快並經過mmap被JVM進行存儲。咱們建議保持默認設置,以動態調整模式爲出發點,將簡化的尺寸與密切監測的應用程序元數據佔有量相結合,從而進行更好的容量規劃。新增一個JVM選項(-XX:MaxMetaspaceSize=<NNN>),可讓您限制分配給class metadata的本地內存。當面臨物理資源(RAM)緊張或相似於內存泄露的狀況時,建議將它做爲一個保障機制。

 

對那種具備larger class metadata footprint或dynamic classloading的Java應用程序,咱們建議經過新的JVM選項調整初始元空間大小 :-XX:MetaspaceSize=<NNN>,例如:1GB。這種調整方法將有助於避免包括class metadata在內的早期垃圾回收,尤爲是在Java應用程序的 「warm-up」期。

Hot Spots

故障診斷和監視

目標 建議
測量和監視應用程序YoungGen和OldGen內存佔用,包括GC活動。爲您的應用程序決定正確的GC策略和Java堆大小。

 

調整應用程序的內存佔用量,如live對象。

分析、監控您所使用的Java分析工具,如JProfiler、Java VisualVM或其餘商業APM產品。容許經過–verbose:gc記錄JVM GC活動。您也可使用相似GCMV(GC Memory Visualizer)的工具查看JVM的暫停時間和內存分配率。

 

性能提示:過多的內存分配率可能意味着須要進行垂直和橫向擴展,或從多個JVM進程中分離出實時數據。

爲了long-lived對象或long-term實時數據考慮,能夠生成並分析JVM heap dump快照。Heap dump分析對於程序內存佔用(retention)的優化是很是有幫助的。

性能提示:因爲從32位到64位,Java應用程序對heap 的需求會比原來高1.5倍。因此,在Java 1.7及如下的版本(這是默認的)中使用 -XX:+UseCompressedOops是很是重要的。這樣的參數調整大大減輕了64位JVM的性能壓力。

調查OutOfMemoryError 問題,尋找OldGen內存泄露的根源。 使用相似Java VisualVM、Plumbr的工具(Java內存泄漏檢測器),分析可能存在的內容泄露。性能提示:要着重分析最大的Java對象上。要意識到下降內存佔有量就意味着提高性能,並下降GC活動。


使用相似 Memory Analyzer的工具生成並分析JVM heap dump快照。

Java併發性

Java併發性能夠定義爲程序同時執行多個任務的能力。對於大型的Java EE系統,這意味着執行多個用戶的業務功能的同時,實現最佳的吞吐量和性能的能力。

不管是硬件能力仍是JVM穩定情況,Java併發性問題可能引發程序的癱瘓,嚴重影響程序的總體性能和可用性。

Thread Lock Contention

當您評估Java應用程序的併發線程的穩定情況時,你會常常遇到Thread lock contention的問題,這是目前最多見的Java併發問題。

例如:Thread lock contention會觸發non-stop,它會嘗試將一個缺乏Java類(ClassNotFoundException的)加載到默認的JDK 1.7 ClassLoader。

若是您在成熟的技術環境中碰見像Thread Dump analysis這樣的問題,咱們強烈建議您積極面對它。這個問題的根源一般不一樣於以前的Java synchronization to legitimate IO blocking或者其餘的non-thread safe calls。Lock contention問題每每是另外一個問題的「症狀」。

Java-level Deadlocks

真正的Java-level deadlocks是不太常見的,它一樣能夠極大程度地影響應用程序的性能和穩定性。當遇到兩個或多個線程永遠阻塞的時候,就會觸發這樣的問題。這種狀況不一樣於其餘常見的那種「day-to-day」線程問題,例如 lock contention、threads waiting on blocking IO calls等等。真正的lock-ordering deadlock問題能夠被看作以下:

Oracle HotSpot 和IBM JVM爲大多數的deadlock detectors狀況提供瞭解決方案,幫助您快速找出形成這種情況的罪魁禍首的線程。遇到相似lock contention troubleshooting的問題,建議從諸如線程轉儲分析爲出發點來解決該問題。

一旦找到形成問題的代碼根源,解決方案涉及lock-ordering條件尋址和來自JDK其餘可用的併發編程技術,如java.util.concurrent.locks.ReentrantLock,提供了諸如tryLock()的方法。這種方法給予開發人員更大的靈活性,也爲防止deadlock和thread lock 「starvation」提供了更多方式。

Clock Time和CPU Burn

在進行JVM調優的同時,也有必要檢查應用程序的行爲,更確切地說是最高clock time和CPU burn的貢獻者。

當Java垃圾回收和線程併發再也不是壓力點,深刻到你的應用程序代碼的執行模式,並專一於頂級響應時間貢獻者(也叫做clock time)是很重要的。檢查應用程序代碼的消CPU耗和Java 線程(CPU burn)也一樣相當重要。CPU使用率較高(>75%)是不正常的(良好的物理資源的利用率)。由於這每每意味着效率低下和容量問題。對於大型的Java EE企業應用,保持安全的CPU緩衝區是必要的,以應對突發的負載衝擊狀況。

摒棄那些傳統的跟蹤方法,如在代碼中加入響應時間「日誌」。Java剖析工具和APM解決方案偏偏能夠幫助您分析這類型的問題。這種方式更加高效、可靠。對於Java生產環境缺少一個強大的APM解決方案。您仍然能夠依賴諸如Java VisualVM的工具,經過多個快照進行thread dump分析,並使用OS CPU分析每一個線程。

最後的建議是,不要妄圖同時解決全部的問題。列出排在最前面的5個clock time和CPU burn問題,而後尋找解決方案。

Application預算

其餘關於Java應用程序性能的重要方面是穩定性和可靠性。在有着99.9%典型可用目標的SLA umbrella下,穩定和可靠對於程序的操做尤其重要。這些系統應該具備高容錯級別,並對應用和資源進行嚴格的預算,以防止發生多米諾效應。用這種方法能夠防止一些這樣的狀況,例如,一個業務流程使用全部可用的物理,中間件或JVM資源。

Hot Spots

超時管理

Java application與外部系統之間缺少合理的超時時間,因爲中間件和JVM線程消耗(blocking IO calls),可能致使嚴重的性能降低和中斷。合理的超時時間能夠避免在遇到外部服務提供商速度緩慢的時候,Java線程等待過久。

 

工具

目標 建議工具
自動、實時地性能監控、調節、預警、趨勢分析、容量管理,等等 Enterprise APM solutions(企業級APM解決方案)注意:APM解決方案提供了工具,這些現成的功能讓您實現如下大部分的Java性能目標。
性能和負載測試 商業性能測試解決方案Apache JMeter

 

http://jmeter.apache.org/

JVM垃圾回收評估,內存分配率和故障排除 Oracle Java VisualVMhttp://docs.oracle.com/javase/8/docs/technotes/guides/visualvm/intro.html

 

http://java.dzone.com/articles/profile-your-applications-java

Oracle Java Mission Control

http://www.oracle.com/technetwork/java/javaseproducts/mission-control/java-mission-control-wp-2008279.pdf

http://www.oracle.com/technetwork/java/javase/jmc53-release-notes-2157171.html

IBM Monitoring and Diagnostic Tools for Java (via IBM Support Assistant tool)

http://www-01.ibm.com/software/support/isa/

JVM verbose:gc logs

JVM argument : -verbose:gc

http://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html

IBM GCMV

https://www.ibm.com/developerworks/java/jdk/tools/gcmv/

JVM堆和類的元數據的內存泄漏分析 Oracle Java VisualVM and Oracle Java Mission ControlIBM Monitoring and Diagnostic Tools for Java

 

Memory Analyzer (heap dump analysis, hprof and phd formats)

https://www.eclipse.org/mat/

https://www.ibm.com/developerworks/java/jdk/tools/memoryanalyzer/

JVM內存分析和堆容量評估 Oracle Java VisualVM and Java Mission ControlIBM Monitoring and Diagnostic Tools for Java

 

Java profilers (JProfiler, YourKit)

http://en.wikipedia.org/wiki/JProfiler

http://www.yourkit.com/

Memory Analyzer (heap dump and application memory footprint analysis)

JVM和中間件併發故障,如thread lock contention和deadlocks Oracle Java VisualVM and Oracle Java Mission Control (threads monitoring, thread dump snapshots)jstack, native OS signal such as kill -3 (thread dump snapshots)

 

http://www.oracle.com/technetwork/java/javase/tooldescr-136044.html#gblfh

IBM Monitoring and Diagnostic Tools for Java

注意:強烈推薦你們關注如何執行一個JVM線程轉儲分析的相關知識。

Java應用程序clock time分析和評測 Oracle Java VisualVM and Oracle Java Mission Control (build-in profiler, sampler and recorder)Java profilers (JProfiler, YourKit)
Java應用程序和線程CPU burn分析 Oracle Java VisualVM and Oracle Java Mission Control (CPU profiler)Java profilers (JProfiler, YourKit)

 

注意:必要的時候,您還能夠依賴JVM線程轉儲和OS CPU每一個線程分析。

Java IO和remoting contention分析,包括超時管理評估和調整 Oracle Java VisualVM and Oracle Java Mission Control(threads monitoring, thread dump snapshots)

 

jstack, native OS signal such as kill -3 (thread dump snapshots)

IBM Monitoring and Diagnostic Tools for Java

注意:強烈推薦你們關注如何執行一個JVM線程轉儲分析的相關知識。

中間件,Java EE容器調整,如線程、JDBC數據源,等等 Oracle Java VisualVM and Oracle Java Mission Control (extra focus on exposed Java EE container runtime MBeans)Java EE container administration and management console
相關文章
相關標籤/搜索