概述:java
Java是目前軟件開發領域中使用最普遍的編程語言之一。Java應用程序在許多垂直領域(銀行、電信、醫療保健等)中都有普遍使用。Refcard的目的是,幫助開發者經過專一於JVM內部,性能調整原則和最佳實踐,以及利用現有監測和故障診斷工具,來提高應用程序在商業環境中的性能。mysql
它能以不一樣的方式定義「optimallinux
performance(最佳性能)」,但基本要素是:Java程序在業務響應時間要求內執行計算任務的能力,程序在高容量下執行業務功能的能力,並具備可靠性高和延遲低的特色。有時,數字自己變得模式化:對於一些大型網站,優秀的頁面響應時間應該在500ms如下。在適當的時候,Refcard包括目標數字。ios
一、背景介紹算法
一個系統的上線除了常規的功能性測試外,還須要通過嚴格的性能測試,知足預期的性能指標(常見的有響應時間,tps等),才容許上生產環境。廣義的性能測試通常還包含負載測試(用於測試系統的容量:即系統在保證必定響應時間的狀況下可以容許多少併發用戶的訪問),壓力測試(用於測試系統的穩定性:即在保證必定壓力的狀況下,查看測試系統的穩定性),併發測試(即測試系統多併發能力:即模擬多用戶訪問同一應用的測試,用於發現併發問題,好比線程鎖,資源競爭,數據庫死鎖等)等。sql
經過性能測試,能夠幫助咱們儘快發現系統的瓶頸。若是發現未能知足預期的業務目標,則須要進行性能調優。性能調優的需求,有時候來自於原型的驗證,有時候來自於生產上實際的問題,無論哪一類的性能調優,咱們通常按照性能監控,性能分析,性能優化這幾個步驟進行。如下章節會對每一個步驟進行詳細分析。數據庫
二、性能監控編程
性能監控是性能調優的第一步,主要目的在於瞭解當前系統運行的狀態,瞭解當前服務器資源使用狀況,JVM的內存使用,線程使用等狀況,以便於第一時間找到瓶頸點。性能優化
2.1 查看服務器配置服務器
爲了更好評估服務器性能,首先應瞭解當前宿主服務器的配置狀況。如下主要是針對linux服務器給出的常見的查看命令。
2.1.1 CPU配置
對於CPU,比較關心的是CPU的總邏輯核數,能夠直接使用mpstat查看。
可使用cat /proc/cpuinfo查看CPU的型號:
2.1.2 內存配置
使用free命令進行查看,能夠看到總的內存,以及使用的狀況
2.1.3 磁盤配置
使用fdisk -l能夠查看到全部的磁盤配置狀況,使用df -TH能夠看到當前磁盤的目錄掛載狀況
有時候,須要確認當前磁盤是否爲SSD盤,判斷cat
/sys/block/*/queue/rotational的返回值(其中*爲你的硬盤設備名稱,例如sda等等),若是返回1則表示磁盤可旋轉,那麼就是HDD了;反之,若是返回0,則表示磁盤不能夠旋轉,那麼就有多是SSD了。以下圖所示,sda是SSD盤。
2.1.4 網絡配置
使用ifconfig命令,能夠看到網卡的配置狀況。有時候,須要查看當前網卡是千M仍是萬M,能夠經過ethtool查看speed能夠判斷。以下圖所示,eth4是千M網卡。
2.2 服務器監控
爲了能實時瞭解系統運行時,資源的佔用狀況,咱們就須要對服務器的系統資源進行監控,如下列出常見的命令以及經常使用的監控事項。
2.2.1 CPU監控
使用vmstat命令,vmstat 2表明每2秒統計一次
重點觀察
Ø Procs中r值,它表明調度程序運行隊列的長度,若是該值長時間大於CPU邏輯核數1倍以上,須要關注,超過3-4倍須要立刻採起行動
Ø System中in(中斷),cs(上下文切換)若是兩值較大,說明系統內核消耗CPU較多
Ø Cpu列中,若是us(用戶態)佔比長期大於50%時,就須要考慮優化算法。根據經驗us+sy佔比參考值爲80%
可使用pidstat -w -I -p pid 2,監控應用的鎖競爭狀況
讓步式上下文切換(cswch)時鐘週期佔用3% ~ 5%,說明Java應用面臨鎖競爭,搶佔式上下文切換率(nvcswch)高,說明預備運行的線程數多於可用的虛擬處理器數。
2.2.2 內存監控
也可使用上述的vmstat查看內存頁面交換,
重點觀察free,si,so這幾列,若是free變小,並且si,so在變化,說明存在內存不足,跟磁盤swap,有發生頁面交換的狀況,須要考慮加大內存。
2.2.3 網絡監控
使用第三方軟件iptraf,它提供了可視化的頁面,經過它能夠實時監控網絡流量狀況。
2.2.4 磁盤
使用iostat進行監控
cpu屬性值說明:
Ø 若是%iowait的值太高,表示硬盤存在I/O瓶頸,%idle值高,表示CPU較空閒,若是%idle值高但系統響應慢時,有多是CPU等待分配內存,此時應加大內存容量。%idle值若是持續低於10,那麼系統的CPU處理能力相對較低,代表系統中最須要解決的資源是CPU。
disk屬性值說明:
Ø 若是%util接近100%,說明產生的I/O請求太多,I/O系統已經滿負荷,該磁盤可能存在瓶頸。若是svctm比較接近await,說明I/O幾乎沒有等待時間;若是await遠大於svctm,說明I/O隊列太長,io響應太慢,則須要進行必要優化。若是avgqu-sz比較大,也表示有當量io在等待。
2.3 JVM監控
使用jdk中自帶的jvisualvm工具,在要鏈接的遠程java進程,啓動時增長jmx的配置,以下:
這樣jvisualvm就能夠經過ip+1111端口偵聽遠程JVM的狀況了。
2.4 鏈接池監控
2.4.1 查看數據庫鏈接池數量
使用netstat –an | grep ‘db ip’ | wc –l命令,能夠看到與數據庫建立的鏈接池,看這個值跟設置的數據庫鏈接池的最小值,以及最大值的關係。若是始終經過最大值,須要考慮調整鏈接的最大值。
2.4.2 查看工做線程數
方法1:使用jvisualvm工具遠程監控來查看
方法2:使用命令查看
2.5 Oracle監控
2.5.1 查看oracle配置
Ø 使用oracle的用戶登陸oracle的服務器(su - oracle)
Ø 啓動sqlplus命令行模式(sqlplus / as sysdba)
Ø 查看配置(Show parameter sga;)
2.5.2 性能監控
Ø 使用sqlplus命令行模式
Ø 開始時啓動快照命令,中止時再執行一遍快速命令
備註:快照命令(exec DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT();)
Ø 快照執行完後,取報告(@?/rdbms/admin/awrrpt)
Ø 分析報告(重點關注top 5 time events)
3 性能分析
3.1 JVM分析
3.1.1 堆分析
爲了避免影響線上的性能,可使用堆轉儲,命令以下:
jmap -dump:live,format=b,file=heap_dump.hprofpid
而後能夠將生成的.hprof文件導入mat,或者jvisualvm進行分析,能夠了解哪些對象正在消耗內存。同時對於識別由建立太多某一特定對象所引起的內存問題,軟件提供的直方圖方法快速且方便。
3.1.2 垃圾回收分析
Jvm啓動時,能夠設置-Xloggc,-XX:PrintGCDetails等參數,開啓gc日誌收集。也可使用jstat進行監控分析,好比jstat–gcutil pid 2用於每隔2秒打印當前Java堆及GC狀況。
3.1.3 線程分析
使用jdk自帶的JMC和jstack工具,能夠查看堵塞的線程。JMC內部集成的JFR能夠很方便的檢索出引起線程堵塞的事件。而jstack在必定程度上能夠檢查線程是堵塞在什麼資源上。如下給出jstack定位思路:
4 性能優化
在深度優化系統前,應該先弄清爲什麼CPU的使用率低。優化代碼的目的是提高而不是下降更短期內的CPU使用率。
4.1 JVM啓動參數優化
4.1.1 原生內存的優化
對原生內存的優化,包含使用壓縮的OOP(jvm啓動參數上增長-XX:+UseCompressedOops)以及調整大內存分頁(同時修改linux配置以及jvm啓動參數-XX:LargePageSizeInBytes)等,均可以提高性能。
4.1.2 垃圾回收機制的優化
Ø 合理設置堆的大小,以及合理設置好代空間的劃分:設置過小容易頻繁GC,而設置太大,GC時停頓時間太長。同時爲了不可能使用到虛擬內存,內存頁交換致使更慢,至少保留1G的物理內存。
Ø 如何選擇各分區大小應該依賴應用程序中對象生命週期的分佈狀況:若是應用存在大量的短時間對象,應該選擇較大的年輕代;若是存在相對較多的持久對象,老年代應該適當增大。
Ø 穩定與震盪的堆大小:將-Xms和-Xmx的大小一致,對垃圾回收有利。
4.1.3 大對象分配優化
Ø 大對象儘可能分配在TLAB,若是大量發生在TLAB外,須要考慮調整TLAB參數,或者減小分配對象的大小。能夠經過-XX:PrintTLAB標誌查看結果。
Ø 大對象劃入老年代:將大對象直接分配到老年代,保持新生代對象的結構的完整性,以提升GC效率,以經過-XX:PretenureSizeThreshold設置進入老年代的閥值。
4.2 java編程優化
由於實際的編程中,涉及性能優化的點比較多,如下只是列舉一些常見的優化項供參考。
4.2.1 線程池優化
Ø 根據當前服務器CPU的數量合理設置最大線程數,最小線程數,線程池任務隊列大小。CPU密集型任務配置儘量小的線程,如配置Ncpu+1個線程的線程池。IO密集型任務則因爲線程並非一直在執行任務,則配置儘量多的線程,如2*Ncpu。
Ø 建議使用有界隊列,有界隊列能增長系統的穩定性和預警能力。
Ø 優先級不一樣的任務可使用優先級隊列PriorityBlockingQueue來處理。
Ø 執行時間不一樣的任務能夠交給不一樣規模的線程池來處理,或者也可使用優先級隊列,讓執行時間短的任務先執行。設置線程的優先級。
4.2.2 其它編程細節
Ø 儘可能減小內存的使用,減小對象的大小,設置類型時考慮最小原則,去掉不用的屬性,以及沒有用到的實例變量。
Ø 經過使用對象池以及線程局部變量的方式來增強對象的重複。對象重用跟GC是有點矛盾,因此主要考慮對象初始化時成本比較高的狀況(即初始化時間比較長)。
Ø 對於保存實際不須要在多個線程間共享的同步對象,同時又在不一樣的實際中進行傳遞的對象,能夠考慮使用線程局部變量,減小同步競爭。
Ø 在一些場景下,優化使用java8並行流的模式
4.3 數據庫優化
4.3.1 使用預編譯
使用preparedStatement方式,重用預處理語句池能夠極大提高性能,同時也要避免出現大量大型對象池化而引發的GC方面的問題。
4.3.2 使用鏈接池
引入hikari鏈接池,在啓動時就配置好
Ø 建立鏈接的代價很大,經過JDBC鏈接池獲取鏈接可省去建立鏈接時間。同時須要合理設置鏈接池的大小。
Ø 合理設值:好比設置檢索時的批量值,設置最優的預取值,設置ResultSet的批量值,能夠提升檢索的性能。
Ø 事務的優化:事務的提交以及事務相關的鎖機制都會影響系統的性能,須要考慮合理設置事務隔離的級別,以及批量提交的策略等。
5 性能實戰經驗彙總
5.1 清算併發性能上不去
5.1.1 問題的現象
使用java8的並行流計算時,發現併發的性能上不去,而且性能會隨着時間推移,不斷的降低
5.1.2 優化點
Ø 引入hikari鏈接池,將單筆延時降到5ms
Ø 關閉日誌
Ø 將sql改爲預編譯的模式
Ø Oracle服務器的把oracle的內存提升
5.2 Hsiar跟中臺的鏈接上,存在不少FIN_WAIT2鏈接
5.3 Server_name是否能夠隨便配置
6 小結
6.1 性能工具箱
6.1.1 壓測工具jmeter
Jmter是開源的壓測工具,也易於上手。它的使用就不介紹了,這裏主要講一些注意的事項:
Ø 它的實時繪圖依賴於服務器端的響應,若是壓測機與服務器時間不一樣步的話,會出現展現圖斷層現象,爲了獲得更準確的性能曲線,建議使用命令行的方式。
Ø 有時候發現壓測性能上不去,有可能的緣由在於客戶端。主要考慮的因素:客戶端的CPU不足以支持所需數量的客戶端線程,或者客戶端須要花大量時間處理響應後才能發送請求。
6.1.2 JVM相關
除了上文提到的jdk自帶的工具外,還有IBM提供的MemoryAnalyzer,以及商用軟件jprofile都很強大。
6.1.3 數據庫相關
對於數據庫的性能監控,可使用Spotlight,它有基於mysql以及oracle的不一樣版本支持。
6.1.4 網絡相關
經常使用Linux自帶的Tcpdump命令導出抓包,而後使用wireshark進行分析,很強大。