java應用服務占用cpu過高,如何優化


 
 

轉載鏈接:

https://blog.csdn.net/weixin_38007185/article/details/107662386?utm_medium=distribute.pc_relevant_bbs_down.none-task-blog-baidujs-2.nonecase&depth_1-utm_source=distribute.pc_relevant_bbs_down.none-task-blog-baidujs-2.nonecase

 

 

當我們cpu使用率高的情況下會出現什么情況?
我們訪問程序的速度比較慢,運行時間長。
系統崩潰,無法訪問程序。
什么情況會導致Java應用程序的CPU使用率飆升?
解決這個問題之前我們先了解幾個知識點:

1.如何計算CPU使用率?

CPU%= 1 - idleTime / sysTime * 100

idleTime:CPU空閑的時間
sysTime:CPU處於用戶模式和內核模式的時間總和
2.與CPU使用率有關的是什么?

人們常說,計算密集型程序的CPU密集程度更高。

那么,JAVA應用程序中的哪些操作更加CPU密集?

以下列出了常見的CPU密集型操作:

頻繁的GC; 如果訪問量很高,可能會導致頻繁的GC甚至FGC。當調用量很大時,內存分配將如此之快以至於GC線程將連續執行,這將導致CPU飆升。
序列化和反序列化。稍后將給出一個示例:當程序執行xml解析時,調用量會增加,從而導致CPU變滿。
序列化和反序列化;
正則表達式。 我遇到了正則表達式使CPU充滿的情況; 原因可能是Java正則表達式使用的引擎實現是NFA自動機,它將在字符匹配期間執行回溯。我寫了一篇文章“ 正則表達式中的隱藏陷阱 ”來詳細解釋原因。
線程上下文切換; 有許多已啟動的線程,這些線程的狀態在Blocked(鎖定等待,IO等待等)和Running之間發生變化。當鎖爭用激烈時,這種情況很容易發生。
有些線程正在執行非阻塞操作,例如while (true)語句。如果在程序中計算需要很長時間,則可以使線程休眠。 
無限循環的while會導致CPU使用率飆升
經常使用Young GC會導致CPU占用率飆升
3. CPU是否與進程和線程相關?

現在,分時操作系統使用循環方式為進程調度分配時間片。如果進程正在等待或阻塞,那么它將不會使用CPU資源。線程稱為輕量級進程,並共享進程資源。因此,線程調度在CPU中也是分時的。但在Java中,我們使用JVM進行線程調度。因此,通常,線程調度有兩種模式:時間共享調度和搶占式調度。

情況1:while的無限循環會導致CPU使用率飆升嗎?

是。

首先,無限循環將調用CPU寄存器進行計數,此操作將占用CPU資源。那么,如果線程始終處於無限循環狀態,CPU是否會切換線程?

除非操作系統時間片到期,否則無限循環不會放棄占用的CPU資源,並且無限循環將繼續向系統請求時間片,直到系統沒有空閑時間來執行任何其他操作。

情況2:頻繁的Young GC會導致CPU占用率飆升嗎?

是。

Young GC本身就是JVM用於垃圾收集的操作,它需要計算內存和調用寄存器。因此,頻繁的Young GC必須占用CPU資源。

讓我們來看一個現實世界的案例。for循環從數據庫中查詢數據集合,然后再次封裝新的數據集合。如果內存不足以存儲,JVM將回收不再使用的數據。因此,如果所需的存儲空間很大,您可能會收到CPU使用率警報。

情況3:具有大量線程的應用程序的CPU使用率是否較高?

不是。

如果通過jstack檢查系統線程狀態時線程總數很大,但處於Runnable和Running狀態的線程數不多,則CPU使用率不一定很高。

我遇到過這樣一種情況:系統線程的數量是1000+,其中超過900個線程處於BLOCKED和WAITING狀態。該線程占用很少的CPU。

但是大多數情況下,如果線程數很大,那么常見的原因是大量線程處於BLOCKED和WAITING狀態。

情況4:對於CPU占用率高的應用程序,線程數是否較大?

不是。

高CPU使用率的關鍵因素是計算密集型操作。如果一個線程中有大量計算,則CPU使用率也可能很高。這也是數據腳本任務需要在大規模集群上運行的原因。

情況5.處於BLOCKED狀態的線程是否會導致CPU占用率飆升?

不會。

CPU使用率的飆升更多是由於上下文切換或過多的可運行狀態線程。處於阻塞狀態的線程不一定會導致CPU使用率上升。

情況6.如果分時操作系統中CPU的值us或sy值很高,這意味着什么?

您可以使用命令查找CPU的值us和sy值top,如以下示例所示:

 

us:用戶空間占用CPU的百分比。簡單來說,高我們是由程序引起的。通過分析線程堆棧很容易找到有問題的線程。
sy:內核空間占用CPU的百分比。當sy為高時,如果它是由程序引起的,那么它基本上是由於線程上下文切換。
由此我們得出結論CPU使用率飆升的兩大原因:

無限循環的while會導致CPU使用率飆升
經常使用Young GC會導致CPU占用率飆
 

4.為什么cpu飆升到100%但是程序還是可以訪問?

我們可以看到的當前服務器的cpu已經達到了接近400%,為什么會達到400%了,因為我們系統是四核的,每個cpu都飆升到100%,所以看到的是接近400%,但是我繼續訪問接口,發現還是可以訪問,原因是什么呢?因為cpu通過時間片來循環執行任務的,多個任務之間是相關切換的,當切換到我們訪問接口的任務時,我們就可以訪問了。但是如果繼續增加訪問達到cpu不能承受的范圍內,系統肯定會崩潰。

 

在linux系統下如何排查cpu使用率飆升的原因?
1、使用top命令查看cpu利用率

如下圖:

 

2、如何查看當前系統的cpu的數量

由於我們現在的系統都不是單核的,都是多核的,每個核中都由一個cpu,我們可以在使用top命令后輸入1就可以看到我們當前系統下的cpu的數量。我們可以看到cpu0和cpu1說明系統是雙核的。如下圖:

 

我們也可以使用通過proc文件系統,直接獲取cpu總數量,具體執行如下命令:cat /proc/cpuinfo  | grep processor 。我們可以到由兩個processor,說明系統是雙核的。如下圖:

 

3、top命令下各個參數的意思

top命令的結果分為兩個部分:

統計信息:前五行是系統整體的統計信息;
進程信息:統計信息下方類似表格區域顯示的是各個進程的詳細信息,默認5秒刷新一次。


統計信息參數介紹

 

進程信息參數介紹

 

在top命令中按f按可以查看顯示的列信息,按對應字母來開啟/關閉列,大寫字母表示開啟,小寫字母表示關閉。帶*號的是默認列。

A: PID = (Process Id) 進程Id;
E: USER = (User Name) 進程所有者的用戶名;
H: PR = (Priority) 優先級
I: NI = (Nice value) nice值。負值表示高優先級,正值表示低優先級
O: VIRT = (Virtual Image (kb)) 進程使用的虛擬內存總量,單位kb。
VIRT=SWAP+RES
Q: RES = (Resident size (kb)) 進程使用的、未被換出的物理內存大小,單位kb。
RES=CODE+DATA
T: SHR = (Shared Mem size (kb)) 共享內存大小,單位kb
W: S = (Process Status) 進程狀態。D=不可中斷的睡眠狀態,R=運行,S=睡眠,T=跟蹤/停止,Z=僵屍進程
K: %CPU = (CPU usage) 上次更新到現在的時間CPU占用百分比
N: %MEM = (Memory usage (RES)) 進程使用的物理內存百分比
M: TIME+ = (CPU Time, hundredths) 進程使用的CPU時間總計,單位1/100秒
b: PPID = (Parent Process Pid) 父進程Id
c: RUSER = (Real user name)
d: UID = (User Id) 進程所有者的用戶id
f: GROUP = (Group Name) 進程所有者的組名
g: TTY = (Controlling Tty) 啟動進程的終端名。不是從終端啟動的進程則顯示為 ?
j: P = (Last used cpu (SMP)) 最后使用的CPU,僅在多CPU環境下有意義
p: SWAP = (Swapped size (kb)) 進程使用的虛擬內存中,被換出的大小,單位kb
l: TIME = (CPU Time) 進程使用的CPU時間總計,單位秒
r: CODE = (Code size (kb)) 可執行代碼占用的物理內存大小,單位kb
s: DATA = (Data+Stack size (kb)) 可執行代碼以外的部分(數據段+棧)占用的物理內存大小,單位kb
u: nFLT = (Page Fault count) 頁面錯誤次數
v: nDRT = (Dirty Pages count) 最后一次寫入到現在,被修改過的頁面數
y: WCHAN = (Sleeping in Function) 若該進程在睡眠,則顯示睡眠中的系統函數名
z: Flags = (Task Flags)任務標志,參考 sched.h
步驟一、找到最耗CPU的進程

執行top -c ,顯示進程運行信息列表,鍵入P (大寫p),進程按照CPU使用率排序,我們可以看到的當前服務器的cpu已經達到了接近400%,記錄PID為38983的進程號,也可以看到是一個java的程序。

 

步驟二:找到最耗CPU的線程

輸入命令top -Hp 38983,顯示一個進程的線程運行信息列表,鍵入P (大寫p),線程按照CPU使用率排序,我么們可以看到占用cpu最高的線程是39060

 

 

步驟三:將線程PID轉化為16進制

輸入命令printf “%x” 39060,所以要轉化為16進制,是因為堆棧里,線程id是用16進制表示的。

 

步驟四:查看堆棧,找到該線程分析

輸入命令jstack 38983| grep ‘0x9894’ -C5 --color,打印進程堆棧,通過線程id,過濾得到線程堆棧​​​​​​​

 

步驟五:根據提示,修改代碼

 

這就證實了while的無限循環會導致CPU使用率飆升。我們在實際開發中要在本地充分的測試,然后再上線,如果線上出現了問題,也不能慌,一定要穩住,要趕緊版本回滾。然后在慢慢排查問題進行修改。


免責聲明!

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



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