1. 程式人生 > >java web伺服器cpu佔用過高的處理

java web伺服器cpu佔用過高的處理

平時專案中有時遇到cpu過高的情況,在此基於自己有限的經驗寫個分享,此處的伺服器都是基於linux平臺。

cpu的佔有執行緒型別總的來說分為兩種:
us :使用者空間佔用CPU百分比
sy :核心空間佔用CPU百分比

一般來講CPU us高的解決方法:
CPU us 高的原因主要是執行執行緒不需要任何掛起動作,且一直執行,導致CPU 沒有機會去排程執行其他的執行緒。
CPU sy高的解決方法:
CPU sy 高的原因主要是執行緒的執行狀態要經常切換,對於這種情況,常見的一種優化方法是減少執行緒數。

我平時具體的步驟如下:

1.得到執行緒最高的幾個id

ps  -eLo pid,lwp,pcpu | grep  15285|sort -nk 3   

2.匯出JAVA執行緒棧資訊

命令:kill -3 [PID] 或者 jstack

3.從棧資訊中找到執行緒數多的幾個

命令:sort  檔名 | uniq -c | sort -nk 1

4.分別分析執行緒數最多的前十個執行緒和執行緒佔用cpu最高的前10個執行緒

這步是最關鍵的,找出這些異常點,比如某個業務功能點佔用較高的cpu或者某種型別的執行緒數量比較多,這個和業務以及具體程式緊密關聯的,在此就不多說了。

5.分析佔用cpu最高的前10個執行緒,結果示例如下:

0x1a5:gc執行緒 "Concurrent Mark-Sweep GC Thread#0" prio=1 tid=0x0000002b29df1400 nid=0x1a5 runnable  0x35f:memcache執行緒 "memcache5-CacheThread" prio=1 tid=0x0000002b3d277de0 nid=0x35f runnable [0x0000000042755000..0x0000000042755c30] 0x35e:memcache執行緒 "memcache1-CacheThread" prio=1 tid=0x0000002b371212f0 nid=0x35e runnable [0x0000000042654000..0x0000000042654cb0] 0x14e9:RMI執行緒 "RMI TCP Connection(102)-10.23.241.59" daemon prio=1 tid=0x0000002b37e72790 nid=0x14e9 runnable [0x000000005103c000..0x000000005103deb0] 0x1be:memcache執行緒 "memcache5-CacheThread" prio=1 tid=0x0000002b3dba4790 nid=0x1be runnable [0x0000000041a48000..0x0000000041a48eb0] 0x1bd:memcache執行緒 "memcache1-CacheThread" prio=1 tid=0x0000002b3fb70340 nid=0x1bd runnable [0x0000000041947000..0x0000000041947b30] 0x1af:jdk編譯執行緒 "CompilerThread1" daemon prio=1 tid=0x0000002b30e11620 nid=0x1af waiting on condition [0x0000000000000000..0x000000004123f7c0] 0x1ae:jdk編譯執行緒 "CompilerThread0" daemon prio=1 tid=0x0000002b30e10280 nid=0x1ae waiting on condition [0x0000000000000000..0x000000004113e440] 0x1684:http請求執行緒,通過該執行緒棧可以看到此處呼叫的是hibernate查詢 "http-8080-Processor123" daemon prio=1 tid=0x0000002b37dfd9d0 nid=0x1684 runnable [0x000000005afd7000..0x000000005afdcd30]

 6.系統優化

如果找出了哪些功能點佔用cpu高,接下來就需要優化了,可以從業務和技術手段兩方面來進行,平時工作中比較常用的技術手段:

彈性時間:對高使用率的請求,分散到不同的時間,比如採用佇列或非同步,減少同一時間處理的請求。

批處理或定時任務:把請求組合成批,這樣可以使得時間真真的都有效的用在了處理上,而不是網路傳輸等準備工作上。

快取:將結果快取起來,空間換時間。

7.如果是gc執行緒比較費時,則需要進一步的定位:

首先檢視一下gc策略是否合理,然後用命令jmap -F -dump:live,file=jmap.hprof [PID] 匯出記憶體dump檔案,我一般每隔5s導一次,一共匯出3次。用Eclipse Memory Analyzer分析匯出來的檔案,分析是哪個類佔用記憶體比較多,分析出可能存在記憶體洩露的地方。

注意jvm分配記憶體時一個大物件的分配比多個小物件的分配效率要低,如果物件比較大,進行拆分能提高效率,具體原因如下:

Java物件所佔用的記憶體主要從堆上進行分配,堆是所有執行緒共享的,因此在堆上分配記憶體時需要進行加鎖,這導致了建立物件開銷比較大。當堆上空間不足時,會觸發GC,如果GC後空間仍然不足,則丟擲OutOfMemory錯誤資訊。Sun JDK為了提升記憶體分配的效率,會為每個新建立的執行緒在新生代的Eden Space上分配一塊獨立的空間,這塊空間稱為TLAB(Thread Local Allocation Buffer),其大小由JVM根據執行情況計算而得,可通過-XX:TLABWasteTargetPercent來設定TLAB可佔用的Eden Space的百分比,預設值為1%。JVM將根據這個比率、執行緒數量及執行緒是否頻繁分配物件來給每個執行緒分配合適大小的TLAB空間 。在TLAB上分配記憶體時不需要加鎖,因此JVM在給執行緒中的物件分配記憶體時會盡量在TLAB上分配,如果物件過大或TLAB空間已用完,則仍然在堆上進行分配,因此在編寫Java程式時,通常多個小的物件比大的物件分配起來更加高效。