Java 應用性能優化是一個程序員必需要考慮的問題,典型的性能問題如頁面響應慢、接口超時,服務器負載高、併發數低,數據庫頻繁死鎖等。
Java應用性能的瓶頸點很是多,好比磁盤、內存、網絡 I/O 等系統因素,Java 應用代碼,JVM GC,數據庫,緩存等。能夠將 Java 性能優化分爲 4 個層級:應用層、數據庫層、框架層、JVM 層,如圖 1 所示。ios
圖 1.Java 性能優化分層模型
每層優化難度逐級增長,涉及的知識和解決的問題也會不一樣。好比應用層須要理解代碼邏輯,經過 Java 線程棧定位有問題代碼行等;數據庫層面須要分析 SQL、定位死鎖等;框架層須要懂源代碼,理解框架機制;JVM 層須要對 GC 的類型和工做機制有深刻了解,對各類 JVM 參數做用瞭然於胸。程序員
1 CPU 診斷
對於 CPU 主要關注平均負載(Load Average),CPU 使用率,上下文切換次數(Context Switch)。
經過 top 命令能夠查看系統平均負載和 CPU 使用率,圖 2 爲經過 top 命令查看某系統的狀態。數據庫
圖 2.top 命令示例
平均負載有三個數字:63.66,58.39,57.18,分別表示過去 1 分鐘、5 分鐘、15 分鐘機器的負載。按照經驗,若數值小於 0.7*CPU 個數,則系統工做正常;若超過這個值,甚至達到 CPU 核數的四五倍,則系統的負載就明顯偏高。
圖 2 中 15 分鐘負載已經高達 57.18,1 分鐘負載是 63.66(系統爲 16 核),說明系統出現負載問題,且存在進一步升高趨勢,須要定位具體緣由了。
經過 vmstat 命令能夠查看 CPU 的上下文切換次數:
上下文切換次數發生的場景主要有以下幾種:
• 1)時間片用完,CPU 正常調度下一個任務;
• 2)被其它優先級更高的任務搶佔;
• 3)執行任務碰到 I/O 阻塞,掛起當前任務,切換到下一個任務;
• 4)用戶代碼主動掛起當前任務讓出 CPU;
• 5)多任務搶佔資源,因爲沒有搶到被掛起;
• 6)硬件中斷。
Java 線程上下文切換主要來自共享資源的競爭。通常單個對象加鎖不多成爲系統瓶頸,除非鎖粒度過大。但在一個訪問頻度高,對多個對象連續加鎖的代碼塊中就可能出現大量上下文切換,成爲系統瓶頸。
好比在咱們系統中就曾出現 log4j 1.x 在較大併發下大量打印日誌,出現頻繁上下文切換,大量線程阻塞,致使系統吞吐量大降的狀況,其相關代碼如清單 1 所示,升級到 log4j 2.x 才解決這個問題。緩存
2 Memory
從操做系統角度,內存關注應用進程是否足夠,可使用 free –m 命令查看內存的使用狀況。
經過 top 命令能夠查看進程使用的虛擬內存 VIRT 和物理內存 RES,根據公式 VIRT = SWAP + RES 能夠推算出具體應用使用的交換分區(Swap)狀況,使用交換分區過大會影響 Java 應用性能,能夠將 swappiness 值調到儘量小。
由於對於 Java 應用來講,佔用太多交換分區可能會影響性能,畢竟磁盤性能比內存慢太多。
3 I/O
I/O 包括磁盤 I/O 和網絡 I/O,通常狀況下磁盤更容易出現 I/O 瓶頸。經過 iostat 能夠查看磁盤的讀寫狀況,經過 CPU 的 I/O wait 能夠看出磁盤 I/O 是否正常。
若是磁盤 I/O 一直處於很高的狀態,說明磁盤太慢或故障,成爲了性能瓶頸,須要進行應用優化或者磁盤更換。
4 Java 應用診斷及工具
應用代碼性能問題是相對好解決的一類性能問題。經過一些應用層面監控報警(UMP),若是肯定有問題的功能和代碼,直接經過代碼就能夠定位;或者經過 top+jstack,找出有問題的線程棧,定位到問題線程的代碼上,也能夠發現問題。對於更復雜,邏輯更多的代碼段,經過 Stopwatch 打印性能日誌每每也能夠定位大多數應用代碼性能問題。
經常使用的 Java 應用診斷包括線程、堆棧、GC 等方面的診斷。
jstack
jstack 命令一般配合 top 使用,經過 top -H -p pid 定位 Java 進程和線程,再利用 jstack -l pid 導出線程棧。因爲線程棧是瞬態的,所以須要屢次 dump,通常 3 次 dump,通常每次隔 5s 就行。將 top 定位的 Java 線程 pid 轉成 16 進制,獲得 Java 線程棧中的 nid,能夠找到對應的問題線程棧。
JProfiler
JProfiler 可對 CPU、堆、內存進行分析,功能強大.性能優化