1. 程式人生 > >Java效能優化全攻略

Java效能優化全攻略

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

介紹

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

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

JVM Internals

基礎知識

程式碼編譯和JIT

編譯Java位元組碼顯然沒有直接從主機執行本機程式碼那麼快。為了提高效能,Hotspot JVM找出最繁忙的位元組碼區域,然後將其編譯成更高效地原生、機器程式碼(自適應優化)。然後這種原生代碼就會儲存在非堆記憶體中的程式碼快取中。

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

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

記憶體空間

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效能目標。

JVM堆和類的元資料的記憶體洩漏分析
JVM記憶體分析和堆容量評估

IBM Monitoring and Diagnostic Tools for Java

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)

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應用程式和執行緒CPU burn分析

Oracle Java VisualVM and Oracle Java Mission Control (CPU profiler)

注意:必要的時候,您還可以依賴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

轉自:https://www.evget.com/article/2016/5/17/24105.html