java性能調優實戰


  在項目壓測過程中,發現系統占用,上下文切換非常頻繁,在此記錄下調優過程,希望對后來人有所幫助。

測試方法:模擬客戶端實際操作,向服務器高並發發送數據,查看服務器的負載情況。

服務器基本配置如下

 

1,基本性能監控工具 top

1) top 使用方式1   top

 

通過top命令,java應用負載極高,系統調用極高(系統調用43% ,而用戶調用只有35%),cpu的大部分資源都被系統消耗了,說明系統某部分存在極不合理的地方。

2) top 使用2;輸入top后 按1,查看cpu各個核的使用情況

 

這個圖說明了 cpu使用分布情況還不錯,即程序線程池配置目前沒有出現問題,如果出現單個cpu,或者幾個cpu 100%,其他空閑的情況,說明線程分配不合理,無法充分利用cpu多核能力。

3) top 使用 3,查看到底是什么線程在忙碌top -Hp 25994

 

如果你仔細觀察就會發現一個有趣的現象,那就是好多線程id就像新出的人民幣一樣,是連着號的,一般來說,他們屬於一組線程。

找到忙碌的線程,那么如何才能映射到java應用中我們自己定義的線程呢?步驟如下

a. jstack 25994 >1  java線程dump出來

b. 將上圖pid列的數字轉換成16進制 比如26157   16進制為0x662d

c. more 1 。然后搜索662d

 

找到了,通過線程名字,可以得出,當前網絡層目前比較忙碌。

注意:給線程起一個好名字非常重要,否則你刀磨的再快,命令玩兒的再溜,依然砍不到人。

2vmstat命令 執行vmstat 1 20查看大約20次數據

 

從這個圖上,我們能夠看出至少三個方面的問題

1) 內核調用 達到50%左右,非常高

2) 上下文切換過於頻繁,每秒達90000次左右

3) 調度隊列線程積壓  此刻的系統已經非常慢了。

3pidstat命令 

查看上下文切換(pidstat 命令屬於sysstat組件的內容,需要自行安裝

pidstat -wt -p 25994  1 10 查看10

 

第一列為線程id

第二列為主動上下文切換

第三列為被動上下文切換

一般來說,我們認為,線程被動上下文切換是正常的,而主動切換可能是發生了io、鎖等情況。對於如此頻繁的上下文切換,我們需要多dump幾次線程,看看如上的線程到底在做什么,dump方法以及操作系統線程id如何映射到java線程,參見上文top -Hp命令。

查看 java線程dump文件,得到如下有效數據

 

應用程序頻繁調用 netty writeAndFlush 方法,從調用棧中我們看到:這個方法實際上是執行了一個系統調用,用於喚醒selectable(多重復用)阻塞線程。

 

netty的讀寫線程在頻繁的與操作系統交互寫數據。

綜上,我們大概得到了兩個結論:

1,應用頻繁的調用nettywriteAndFlush方法,這可能會產生大量的系統調用.

2netty頻繁的與操作系統交互進行io操作。上文說過,io操作可能會導致線程主動切換上下文。

於是乎,兩個優化思路也呼之欲出,如果writeAndFlush方法會產出大量的中斷,那么netty還有沒有提供其他的方法?第二個問題,io操作太過頻繁,那么可不可以把消息稍微合並一下以減少io的頻繁度呢?(當然這種思路需要結合實際項目,我們項目的特點是小包特別頻繁)。沿着這兩個思路,重新查看了netty相關部分的源碼,幸運的是找到了突破口,

 

 

這兩個方法天生就是一起用的,write方法可以先把數據記到內存中,等隨后的flush操作把內存中所有的數據一次刷新到操作系統中。這種操作完全符合預期,即避免了系統調用,用完成了數據的 打包,美妙的是這種打包對應用而言是透明的。

調用改進策略

對於消息的發送,我們封裝了統一接口,如下

 

這個接口被大量調用,分布於應用的各個“角落”,改變每一個調用顯然是不可能的。並且,應用層是無法明確知道在何時讓消息“等一會”再統一發送出去的。換句話說,就是在不改變現有調用的情況下,將這種優化“神不知鬼不覺”的添加進去。根據我們的線程分配策略,我的解決思路是在一次線程調用結束后統一發送本次調用所有消息。即將需要發送消息的Channel先存在ThreadLocal中,然后,統一操作代碼片段如下

① 按線程存儲 channel集合

 

② 設置開關,有些線程不需要做合並

 

③ 發消息時,只需要把開啟這個功能的channel存起來即可。

 

④ 消息統一發送

 

有效代碼不超過20行,然后我們看一下結果vmstat 1

 

為方便觀察結果,我把上圖貼下來一起對比

 

系統調用,上下文切換,調用隊列三項指標都有顯著的改善,cpu使用率提升了20%左右(觀察上圖的id列,下圖的空閑百分比為10%左右,而上圖在30%左右)。

總結:操作系統自身提供的工具,有着無與倫比的威力,再結合jdk提供的幾個常用命令,如jstack(線程)、jmap(內存)、jstat(垃圾回收)等,能夠幫助快速幫助我們定位問題。一般來說,操作系統監控命令能夠幫助我們確定,應用到底有沒有問題(諸如,cpu使用率、內存占用情況、網絡、磁盤、調度隊列等等),jdk工具能夠進一步幫助我們定位問題出現在哪(線程分配、jvm堆大小配置、等等)。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM